commit 676002a0fb8cc71323bc13b5612f824140fdeedf parent 75982aad67276e551305dc6e0696ed4990763949 Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com> Date: Mon, 4 Aug 2025 18:42:41 +0200 small patch Diffstat:
23 files changed, 652 insertions(+), 234 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 @@ -133,7 +133,7 @@ enum PayPhase /** * Process the donation receipt response from DONAU (save the donau_sigs to the db). */ - PP_PROCESS_DONATION_RECEIPT, + PP_FINAL_OUTPUT_TOKEN_PROCESSING, /** * Notify other processes about successful payment. @@ -1770,6 +1770,7 @@ build_token_sigs (struct PayContext *pc) GNUNET_assert (NULL != token_sigs); for (unsigned int i = 0; i < pc->validate_tokens.output_tokens_len; i++) { + /* FIXME: Do we want to output hash? */ GNUNET_assert (0 == json_array_append_new ( token_sigs, @@ -1822,9 +1823,6 @@ phase_success_response (struct PayContext *pc) GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_array_steal ("token_sigs", token_sigs)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("donau_sigs", - pc->donau_receipt.donau_sigs_json)), GNUNET_JSON_pack_data_auto ("sig", &sig))); GNUNET_free (pos_confirmation); @@ -1955,100 +1953,97 @@ signatures_to_json (size_t num_sig, * @param pc payment context */ static void -phase_process_donation_receipt (struct PayContext *pc) +phase_final_output_token_processing (struct PayContext *pc) { -#ifndef HAVE_DONAU_DONAU_SERVICE_H - /* Theoretically impossible, yet just to be safe */ - pc->phase = PP_PAYMENT_NOTIFICATION; - return; -#else - if (pc->parse_wallet_data.num_bkps != - pc->donau_receipt.num_sigs) - { /* Not supposed to happen, as it is already checked by donau c-api, but just in case */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Mismatch in wallet_data.num_bkps %llu and " - "donau_receipt.num_sigs %llu", - (unsigned long long) pc->parse_wallet_data.num_bkps, - (unsigned long long) pc->donau_receipt.num_sigs); - pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Mismatch between blinded key pairs and donation signatures received from donau")); - return; - } - - if ( (NULL == pc->donau_receipt.donau_sigs_json) || - (! signatures_to_json (pc->donau_receipt.num_sigs, - pc->donau_receipt.sigs, - pc->donau_receipt.donau_sigs_json)) ) +#ifdef HAVE_DONAU_DONAU_SERVICE_H + if (pc->parse_wallet_data.num_bkps > 0) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to convert Donau signatures to JSON (order %s)", - pc->order_id); - pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_JSON_INVALID, - "donau signature JSON creation failure")); - return; - } + if (pc->parse_wallet_data.num_bkps != + pc->donau_receipt.num_sigs) + { /* Not supposed to happen, as it is already checked by donau c-api, but just in case */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Mismatch in wallet_data.num_bkps %llu and " + "donau_receipt.num_sigs %llu", + (unsigned long long) pc->parse_wallet_data.num_bkps, + (unsigned long long) pc->donau_receipt.num_sigs); + pay_end (pc, + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "Mismatch between blinded key pairs and donation signatures received from donau")); + return; + } - /* Attaching first sig from the donau as output_token signature, - * to reference the output_token related to donau_sigs */ - uint64_t d_out = -1; - const struct TALER_MERCHANT_ContractChoice *choice = - &pc->check_contract.contract_terms->details.v1 - .choices[pc->parse_wallet_data.choice_index]; - - for (size_t i = 0; i < pc->validate_tokens.output_tokens_len; i++) - if (i < choice->outputs_len && - TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT == - choice->outputs[i].type) + if ( (NULL == pc->donau_receipt.donau_sigs_json) || + (! signatures_to_json (pc->donau_receipt.num_sigs, + pc->donau_receipt.sigs, + pc->donau_receipt.donau_sigs_json)) ) { - d_out = i; - break; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to convert Donau signatures to JSON (order %s)", + pc->order_id); + pay_end (pc, + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_JSON_INVALID, + "donau signature JSON creation failure")); + return; } - GNUNET_assert (-1 != d_out); - { - uint64_t ot_len - = pc->validate_tokens.output_tokens_len; - uint64_t ot_len_with_donau_sig - = pc->validate_tokens.output_tokens_len + pc->donau_receipt.num_sigs - 1; + /* Attaching sigs from the donau to output_tokens*/ + uint64_t d_out = -1; + const struct TALER_MERCHANT_ContractChoice *choice = + &pc->check_contract.contract_terms->details.v1 + .choices[pc->parse_wallet_data.choice_index]; + + for (size_t i = 0; i < pc->validate_tokens.output_tokens_len; i++) + if (i < choice->outputs_len && + TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT == + choice->outputs[i].type) + { + d_out = i; + break; + } + GNUNET_assert (-1 != d_out); - if (pc->donau_receipt.num_sigs > 1) { - GNUNET_array_grow (pc->validate_tokens.output_tokens, - pc->validate_tokens.output_tokens_len, - ot_len_with_donau_sig); + uint64_t ot_len + = pc->validate_tokens.output_tokens_len; + uint64_t ot_len_with_donau_sig + = pc->validate_tokens.output_tokens_len + pc->donau_receipt.num_sigs + - 1; - memmove ( - &pc->validate_tokens.output_tokens[d_out + pc->donau_receipt.num_sigs], - &pc->validate_tokens.output_tokens[d_out + 1], - (ot_len - d_out - 1) * sizeof (struct SignedOutputToken)); - } + if (pc->donau_receipt.num_sigs > 1) + { + GNUNET_array_grow (pc->validate_tokens.output_tokens, + pc->validate_tokens.output_tokens_len, + ot_len_with_donau_sig); + + memmove ( + &pc->validate_tokens.output_tokens[d_out + + pc->donau_receipt.num_sigs], + &pc->validate_tokens.output_tokens[d_out + 1], + (ot_len - d_out - 1) * sizeof (struct SignedOutputToken)); + } - for (unsigned int i = 0; i < pc->donau_receipt.num_sigs; i++) - { - struct SignedOutputToken *ot = - &pc->validate_tokens.output_tokens[d_out + i]; + for (unsigned int i = 0; i < pc->donau_receipt.num_sigs; i++) + { + struct SignedOutputToken *ot = + &pc->validate_tokens.output_tokens[d_out + i]; - ot->sig.signature = pc->donau_receipt.sigs[i].blinded_sig; - ot->h_issue.hash = - pc->parse_wallet_data.bkps[i].h_donation_unit_pub.hash; + ot->sig.signature = pc->donau_receipt.sigs[i].blinded_sig; + ot->h_issue.hash = + pc->parse_wallet_data.bkps[i].h_donation_unit_pub.hash; + } + pc->validate_tokens.output_tokens_len = ot_len_with_donau_sig; } - pc->validate_tokens.output_tokens_len = ot_len_with_donau_sig; } - - - /* To have the possibility to fetch the donau_sigs - * in case of contract_paid, we need to save - * the sigs[0].blinded_sig and donau_receipt.donau_sigs_json - * to db */ +#endif + /* Part of saving the output_token sigs so that on re-pay we can fetch them from db*/ enum GNUNET_DB_QueryStatus qs; unsigned int retry; + TMH_db->preflight (TMH_db->cls); if (GNUNET_OK != TMH_db->start (TMH_db->cls, "insert_order_blinded_sigs")) @@ -2063,27 +2058,34 @@ phase_process_donation_receipt (struct PayContext *pc) for (retry = 0; retry < MAX_RETRIES; retry++) { - qs = TMH_db->insert_order_blinded_sigs ( - TMH_db->cls, - pc->order_id, - pc->donau_receipt.donau_sigs_json, - pc->donau_receipt.sigs[0].blinded_sig); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + for (unsigned int i = 0; + i < pc->validate_tokens.output_tokens_len; + i++) { - TMH_db->rollback (TMH_db->cls); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Retrying insert blinded donation receipts"); - continue; - } - if (0 >= qs) - { - TMH_db->rollback (TMH_db->cls); - pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert blinded donation receipts")); - return; + qs = TMH_db->insert_order_blinded_sigs ( + TMH_db->cls, + pc->order_id, + &pc->validate_tokens.output_tokens[i].h_issue.hash, + pc->validate_tokens.output_tokens[i].sig.signature); + + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + TMH_db->rollback (TMH_db->cls); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Retrying insert blinded token signature"); + continue; + } + + if (0 >= qs) + { + TMH_db->rollback (TMH_db->cls); + pay_end (pc, + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert blinded donation receipts")); + return; + } } qs = TMH_db->commit (TMH_db->cls); @@ -2115,9 +2117,7 @@ phase_process_donation_receipt (struct PayContext *pc) "insert blinded donation receipts retry exhausted")); return; } - pc->phase = PP_PAYMENT_NOTIFICATION; -#endif } @@ -2171,7 +2171,7 @@ merchant_donau_issue_receipt_cb (void *cls, resp->details.ok.num_blinded_sigs; pc->donau_receipt.donau_sigs_json = json_array (); - pc->phase = PP_PROCESS_DONATION_RECEIPT; + pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING; pay_resume (pc); return; @@ -2238,7 +2238,7 @@ phase_request_donation_receipt (struct PayContext *pc) { #ifndef HAVE_DONAU_DONAU_SERVICE_H /* If Donau is disabled at compile-time, skip. */ - pc->phase = PP_PAYMENT_NOTIFICATION; + pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING; return; #else pc->donau_receipt.birh = @@ -2890,7 +2890,7 @@ phase_execute_pay_transaction (struct PayContext *pc) break; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: /* We skip outputs of donation receipts here, as they are handled in the - * phase_process_donation_receipt() callback from donau */ + * phase_final_output_token_processing() callback from donau */ break; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: struct SignedOutputToken *output = @@ -2987,15 +2987,16 @@ phase_execute_pay_transaction (struct PayContext *pc) } } - /* Ternary iterator */ if (NULL != pc->parse_wallet_data.donau.donau_url) { - /* We have a Donau URL => we do the new donation receipt phase. */ pc->phase = PP_REQUEST_DONATION_RECEIPT; } + else if (pc->validate_tokens.output_tokens_len > 0) + { + pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING; + } else { - /* No Donau URL, skip donation phase. */ pc->phase = PP_PAYMENT_NOTIFICATION; } } @@ -3684,6 +3685,29 @@ input_tokens_paid_check ( /** + * Small helper function to append an output token signature from db + * + * @param cls closure with `struct PayContext *` + * @param h_issue hash of the token + * @param sig signature of the token + */ +static void +append_output_token_sig (void *cls, + struct GNUNET_HashCode *h_issue, + struct GNUNET_CRYPTO_BlindedSignature *sig) +{ + struct PayContext *pc = cls; + struct SignedOutputToken out; + + out.h_issue.hash = *h_issue; + out.sig.signature = sig; + GNUNET_array_append (pc->validate_tokens.output_tokens, + pc->validate_tokens.output_tokens_len, + out); +} + + +/** * Handle case where contract was already paid. Either decides * the payment is idempotent, or refunds the excess payment. * @@ -3753,20 +3777,17 @@ phase_contract_paid (struct PayContext *pc) unmatched = true; } - /* In this part we are fetching donau related outputs */ + /* In this part we are fetching token_sigs related output */ { - /* FIXME: stored_sig can be replaced with reference to - * the output_token, if output_tokens also processed - * in contract_paid case */ - struct GNUNET_CRYPTO_BlindedSignature *stored_sig = - GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); enum GNUNET_DB_QueryStatus qs; + qs = TMH_db->select_order_blinded_sigs ( TMH_db->cls, pc->order_id, - &pc->donau_receipt.donau_sigs_json, - stored_sig); - if (qs < 0) + &append_output_token_sig, + pc + ); + if (0 > qs) { GNUNET_break (0); pay_end (pc, @@ -3777,8 +3798,8 @@ phase_contract_paid (struct PayContext *pc) "select_order_blinded_sigs")); return; } - GNUNET_free (stored_sig); } + if (! unmatched) { /* Everything fine, idempotent request, generate response immediately */ @@ -4762,8 +4783,8 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh, case PP_REQUEST_DONATION_RECEIPT: phase_request_donation_receipt (pc); break; - case PP_PROCESS_DONATION_RECEIPT: - phase_process_donation_receipt (pc); + case PP_FINAL_OUTPUT_TOKEN_PROCESSING: + phase_final_output_token_processing (pc); break; case PP_PAYMENT_NOTIFICATION: phase_payment_notification (pc); diff --git a/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.c b/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.c @@ -15,7 +15,7 @@ */ /** * @file taler-merchant-httpd_private-delete-donau-instance-ID.c - * @brief implement DELETE /private/donau/$charity_id + * @brief implement DELETE /private/donau/$donau_serial_id * @author Bohdan Potuzhnyi * @author Vlada Svirsh */ @@ -27,7 +27,7 @@ #include <taler/taler_dbevents.h> /** - * Handle a DELETE "/donau/$charity_id/" request. + * Handle a DELETE "/donau/$donau_serial_id/" request. * * @param rh context of the handler * @param connection the MHD connection to handle @@ -41,14 +41,14 @@ TMH_private_delete_donau_instance_ID (const struct TMH_RequestHandler *rh, { struct TMH_MerchantInstance *mi = hc->instance; enum GNUNET_DB_QueryStatus qs; - uint64_t charity_id; + uint64_t donau_serial_id; char dummy; GNUNET_assert (NULL != mi); if (1 != sscanf (hc->infix, "%lu%c", - &charity_id, + &donau_serial_id, &dummy)) { return TALER_MHD_reply_with_error (connection, @@ -58,8 +58,8 @@ TMH_private_delete_donau_instance_ID (const struct TMH_RequestHandler *rh, } qs = TMH_db->delete_donau_instance (TMH_db->cls, - &hc->instance->merchant_pub, - charity_id); + hc->instance->settings.id, + donau_serial_id); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: diff --git a/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.h b/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.h @@ -27,7 +27,7 @@ /** - * Handle a DELETE "/donau/$charity_id/" request. + * Handle a DELETE "/donau/$donau_serial_id/" request. * * @param rh context of the handler * @param connection the MHD connection to handle diff --git a/src/backend/taler-merchant-httpd_private-get-donau-instances.c b/src/backend/taler-merchant-httpd_private-get-donau-instances.c @@ -32,6 +32,7 @@ * Add details about a Donau instance to the JSON array. * * @param cls json array to which the Donau instance details will be added + * @param donau_instance_serial the serial number of the Donau instance * @param donau_url the URL of the Donau instance * @param charity_name the name of the charity * @param charity_pub_key the public key of the charity @@ -43,6 +44,7 @@ */ static void add_donau_instance (void *cls, + uint64_t donau_instance_serial, const char *donau_url, const char *charity_name, const struct DONAU_CharityPublicKeyP *charity_pub_key, @@ -58,6 +60,8 @@ add_donau_instance (void *cls, 0 == json_array_append_new ( json_instances, GNUNET_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("donau_instance_serial", + donau_instance_serial), GNUNET_JSON_pack_string ("donau_url", donau_url), GNUNET_JSON_pack_string ("charity_name", @@ -96,7 +100,7 @@ TMH_private_get_donau_instances (const struct TMH_RequestHandler *rh, TMH_db->preflight (TMH_db->cls); qs = TMH_db->select_donau_instances (TMH_db->cls, - &hc->instance->merchant_pub, + hc->instance->settings.id, &add_donau_instance, json_donau_instances); if (0 > qs) diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -3355,6 +3355,7 @@ add_donau_output (struct OrderContext *oc, struct TALER_MERCHANT_ContractOutput *output) { enum GNUNET_DB_QueryStatus qs; + qs = TMH_db->select_donau_instances_filtered ( TMH_db->cls, output->details.donation_receipt.amount.currency, diff --git a/src/backenddb/merchant-0022.sql b/src/backenddb/merchant-0022.sql @@ -73,22 +73,22 @@ COMMENT ON COLUMN merchant_donau_instances.charity_receipts_to_date COMMENT ON COLUMN merchant_donau_instances.current_year IS 'The current year for tracking donations for this instance, stored as an 8-byte integer'; -CREATE TABLE IF NOT EXISTS merchant_order_donau -(order_donau_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY +CREATE TABLE IF NOT EXISTS merchant_order_token_blinded_sigs +(order_token_bs_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,order_serial BIGINT NOT NULL REFERENCES merchant_contract_terms (order_serial) ON DELETE CASCADE - ,donau_blinded_sigs_json TEXT NOT NULL - ,donau_blinded_signature BYTEA NOT NULL CHECK (LENGTH(donau_blinded_signature)=48) + ,token_blinded_signature BYTEA NOT NULL CHECK (LENGTH(token_blinded_signature)=48) + ,token_hash BYTEA NOT NULL CHECK (LENGTH(token_hash)=64) ); -COMMENT ON TABLE merchant_order_donau +COMMENT ON TABLE merchant_order_token_blinded_sigs IS 'Table linking merchant orders with Donau BUDIS information'; -COMMENT ON COLUMN merchant_order_donau.order_donau_serial - IS 'Unique serial identifier for Donau order linkage'; -COMMENT ON COLUMN merchant_order_donau.order_serial +COMMENT ON COLUMN merchant_order_token_blinded_sigs.order_token_bs_serial + IS 'Unique serial identifier for token order linkage'; +COMMENT ON COLUMN merchant_order_token_blinded_sigs.order_serial IS 'Foreign key linking to the corresponding merchant order'; -COMMENT ON COLUMN merchant_order_donau.donau_blinded_sigs_json - IS 'Donau blinded sigs json associated with the order'; -COMMENT ON COLUMN merchant_order_donau.donau_blinded_signature - IS 'First blinded signature returned by Donau, raw 64-byte value'; +COMMENT ON COLUMN merchant_order_token_blinded_sigs.token_blinded_signature + IS 'Blinded signature of the token associated with the order'; +COMMENT ON COLUMN merchant_order_token_blinded_sigs.token_hash + IS 'Hash of the token'; COMMIT; \ No newline at end of file diff --git a/src/backenddb/pg_delete_donau_instance.c b/src/backenddb/pg_delete_donau_instance.c @@ -29,13 +29,13 @@ enum GNUNET_DB_QueryStatus TMH_PG_delete_donau_instance ( void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const uint64_t charity_id) + const char *id, + const uint64_t donau_serial_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&charity_id), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_uint64 (&donau_serial_id), + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; @@ -45,8 +45,8 @@ TMH_PG_delete_donau_instance ( "DELETE FROM merchant_donau_instances di" " USING merchant_instances mi" " WHERE di.merchant_instance_serial = mi.merchant_serial" - " AND di.charity_id = $1" - " AND mi.merchant_pub = $2;"); + " AND di.donau_instances_serial = $1" + " AND mi.merchant_id = $2;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_donau_instance", params); diff --git a/src/backenddb/pg_delete_donau_instance.h b/src/backenddb/pg_delete_donau_instance.h @@ -30,14 +30,14 @@ * Delete an existing Donau charity instance from the database. * * @param cls closure - * @param merchant_pub merchant_pub_key that we use as reference for the merchant_instance + * @param id merchant instance id * @param charity_id unique identifier of the charity instance to be deleted * @return transaction status code */ enum GNUNET_DB_QueryStatus TMH_PG_delete_donau_instance ( void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *id, const uint64_t charity_id); #endif diff --git a/src/backenddb/pg_insert_order_blinded_sigs.c b/src/backenddb/pg_insert_order_blinded_sigs.c @@ -29,13 +29,13 @@ enum GNUNET_DB_QueryStatus TMH_PG_insert_order_blinded_sigs ( void *cls, const char *order_id, - const json_t *blinded_sigs, + const struct GNUNET_HashCode *hash, const struct GNUNET_CRYPTO_BlindedSignature *blind_sig) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (order_id), - TALER_PQ_query_param_json (blinded_sigs), + GNUNET_PQ_query_param_auto_from_type (hash), GNUNET_PQ_query_param_auto_from_type (blind_sig), GNUNET_PQ_query_param_end }; @@ -43,8 +43,8 @@ TMH_PG_insert_order_blinded_sigs ( check_connection (pg); PREPARE (pg, "insert_blinded_sigs", - "INSERT INTO merchant_order_donau" - " (order_serial, donau_blinded_sigs_json, donau_blinded_signature)" + "INSERT INTO merchant_order_token_blinded_sigs" + " (order_serial, token_hash, token_blinded_signature)" " SELECT order_serial, $2, $3" " FROM merchant_contract_terms" " WHERE order_id = $1"); diff --git a/src/backenddb/pg_insert_order_blinded_sigs.h b/src/backenddb/pg_insert_order_blinded_sigs.h @@ -31,7 +31,7 @@ * * @param cls closure (our `struct PostgresClosure *`) * @param order_id business-level order identifier - * @param blinded_sigs JSON with Donau blinded signature(s) + * @param hash hash of the blinded_sig * @param blind_sig blinded signature of the first blinded_sigs * @return GNUNET_DB status code */ @@ -39,7 +39,7 @@ enum GNUNET_DB_QueryStatus TMH_PG_insert_order_blinded_sigs ( void *cls, const char *order_id, - const json_t *blinded_sigs, + const struct GNUNET_HashCode *hash, const struct GNUNET_CRYPTO_BlindedSignature *blind_sig); #endif diff --git a/src/backenddb/pg_select_donau_instances.c b/src/backenddb/pg_select_donau_instances.c @@ -66,6 +66,7 @@ select_donau_instance_cb (void *cls, for (unsigned int i = 0; i < num_results; i++) { + uint64_t donau_instance_serial; char *donau_url; char *charity_name; struct DONAU_CharityPublicKeyP charity_pub_key; @@ -76,6 +77,8 @@ select_donau_instance_cb (void *cls, json_t *donau_keys_json; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("donau_instances_serial", + &donau_instance_serial), GNUNET_PQ_result_spec_string ("donau_url", &donau_url), GNUNET_PQ_result_spec_string ("charity_name", @@ -105,6 +108,7 @@ select_donau_instance_cb (void *cls, return; } sdc->cb (sdc->cb_cls, + donau_instance_serial, donau_url, charity_name, &charity_pub_key, @@ -120,8 +124,7 @@ select_donau_instance_cb (void *cls, enum GNUNET_DB_QueryStatus TMH_PG_select_donau_instances (void *cls, - const struct - TALER_MerchantPublicKeyP *merchant_pub, + const char *id, TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls) { @@ -134,7 +137,7 @@ TMH_PG_select_donau_instances (void *cls, }; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; @@ -143,7 +146,8 @@ TMH_PG_select_donau_instances (void *cls, PREPARE (pg, "select_donau_instances", "SELECT" - " di.donau_url" + " di.donau_instances_serial" + ",di.donau_url" ",di.charity_name" ",mi.merchant_pub AS charity_pub_key" ",di.charity_id" @@ -154,7 +158,7 @@ TMH_PG_select_donau_instances (void *cls, " FROM merchant_donau_instances di" " JOIN merchant_donau_keys dk ON di.donau_url = dk.donau_url" " JOIN merchant_instances mi ON di.merchant_instance_serial = mi.merchant_serial" - " WHERE mi.merchant_pub = $1"); + " WHERE mi.merchant_id = $1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, diff --git a/src/backenddb/pg_select_donau_instances.h b/src/backenddb/pg_select_donau_instances.h @@ -32,7 +32,7 @@ * Select multiple Donau instances from the database. * * @param cls the closure for the database context - * @param merchant_pub merchant_pub to select only instance related instances + * @param id the ID of the merchant instance * @param cb callback function to call with each result * @param cb_cls closure for the callback * @return status of the PG @@ -40,7 +40,7 @@ enum GNUNET_DB_QueryStatus TMH_PG_select_donau_instances ( void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *id, TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls); diff --git a/src/backenddb/pg_select_order_blinded_sigs.c b/src/backenddb/pg_select_order_blinded_sigs.c @@ -25,40 +25,102 @@ #include "pg_select_order_blinded_sigs.h" #include "pg_helper.h" +/** + * @brief Context for the callback to handle the result of the blinded sigs selection. + */ +struct SelectBlindedSigsContext +{ + /** + * @brief Callback to be called with the result of the blinded sigs selection. + */ + void *cb_cls; + + /** + * @brief Callback to be called with the result of the blinded sigs selection. + */ + TALER_MERCHANTDB_BlindedSigCallback cb; + + /** + * @brief Result status of the query. + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * @brief Callback to handle the result of the blinded sigs selection. + * + * @param cls the closure containing the callback and its context + * @param result the result of the query + * @param num_results number of results in the result set + */ +static void +restore_sig_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SelectBlindedSigsContext *ctx = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + struct GNUNET_HashCode h_issue; + struct GNUNET_CRYPTO_BlindedSignature sig; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("token_hash", + &h_issue), + GNUNET_PQ_result_spec_auto_from_type ("token_blinded_signature", + &sig), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != GNUNET_PQ_extract_result (result, + rs, + i)) + { + ctx->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + ctx->cb (ctx->cb_cls, + &h_issue, + &sig); + GNUNET_PQ_cleanup_result (rs); + } +} + enum GNUNET_DB_QueryStatus TMH_PG_select_order_blinded_sigs ( void *cls, const char *order_id, - json_t **blinded_sigs, - struct GNUNET_CRYPTO_BlindedSignature *blind_sig_out) + TALER_MERCHANTDB_BlindedSigCallback cb, + void *cb_cls) { struct PostgresClosure *pg = cls; + struct SelectBlindedSigsContext ctx = { + .cb = cb, + .cb_cls = cb_cls, + .qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("donau_blinded_sigs_json", - blinded_sigs), - GNUNET_PQ_result_spec_auto_from_type ("donau_blinded_signature", - blind_sig_out), - GNUNET_PQ_result_spec_end - }; check_connection (pg); PREPARE (pg, "select_blinded_sigs", "SELECT" - " mod.donau_blinded_sigs_json" - " ,mod.donau_blinded_signature" - " FROM merchant_order_donau AS mod" + " motbs.token_blinded_signature" + " ,motbs.token_hash" + " FROM merchant_order_token_blinded_sigs AS motbs" " JOIN merchant_contract_terms AS mct USING (order_serial)" " WHERE mct.order_id = $1"); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "select_blinded_sigs", - params, - rs); + return GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + "select_blinded_sigs", + params, + &restore_sig_cb, + &ctx); } diff --git a/src/backenddb/pg_select_order_blinded_sigs.h b/src/backenddb/pg_select_order_blinded_sigs.h @@ -33,15 +33,15 @@ * * @param cls closure (our `struct PostgresClosure *`) * @param order_id business-level order identifier - * @param[out] blinded_sigs where to write the JSON object (refcount +1) - * @param[out] blind_sig_out first signature of the blinded_sigs + * @param cb callback to be called with the blinded signature and hash + * @param cb_cls callback closure * @return GNUNET_DB status code */ enum GNUNET_DB_QueryStatus TMH_PG_select_order_blinded_sigs ( void *cls, const char *order_id, - json_t **blinded_sigs, - struct GNUNET_CRYPTO_BlindedSignature *blind_sig_out); + TALER_MERCHANTDB_BlindedSigCallback cb, + void *cb_cls); #endif diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -89,6 +89,7 @@ #include "pg_lookup_orders.h" #include "pg_insert_order.h" #include "pg_insert_order_budis.h" +#include "pg_insert_order_blinded_sigs.h" #include "pg_unlock_inventory.h" #include "pg_insert_order_lock.h" #include "pg_select_order_blinded_sigs.h" @@ -174,7 +175,6 @@ #include "pg_lookup_order_charity.h" #include "pg_upsert_donau_keys.h" #include "pg_update_donau_instance.h" -#include "pg_insert_order_blinded_sigs.h" #endif /** @@ -489,6 +489,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_insert_order; plugin->insert_order_budis = &TMH_PG_insert_order_budis; + plugin->insert_order_blinded_sigs + = &TMH_PG_insert_order_blinded_sigs; plugin->unlock_inventory = &TMH_PG_unlock_inventory; plugin->insert_order_lock @@ -688,8 +690,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_upsert_donau_keys; plugin->update_donau_instance = &TMH_PG_update_donau_instance; - plugin->insert_order_blinded_sigs - = &TMH_PG_insert_order_blinded_sigs; #endif return plugin; } diff --git a/src/include/taler_merchant_donau.h b/src/include/taler_merchant_donau.h @@ -78,6 +78,11 @@ struct TALER_MERCHANT_DONAU_Charity struct TALER_MERCHANTDB_DonauInstance { /** + * Donau instance serial + */ + uint64_t donau_instance_serial; + + /** * The URL for the Donau instance. */ char *donau_url; @@ -128,6 +133,7 @@ struct TALER_MERCHANTDB_DonauInstance * the details of each Donau instance retrieved from the database. * * @param cls Closure to pass additional context or data to the callback function. + * @param donau_instance_serial Serial number of the Donau instance in the merchant database. * @param donau_url The URL of the Donau instance. * @param charity_name The name of the charity associated with the Donau instance. * @param charity_pub_key Pointer to the charity's public key used for cryptographic operations. @@ -140,6 +146,7 @@ struct TALER_MERCHANTDB_DonauInstance typedef void (*TALER_MERCHANTDB_DonauInstanceCallback)( void *cls, + uint64_t donau_instance_serial, const char *donau_url, const char *charity_name, const struct DONAU_CharityPublicKeyP *charity_pub_key, @@ -178,6 +185,11 @@ struct TALER_MERCHANT_DonauInstanceGetHandle; struct TALER_MERCHANT_DonauInstanceEntry { /** + * Serial number of the Donau instance in merchant database. + */ + uint64_t donau_instance_serial; + + /** * The URL of the Donau service. */ const char *donau_url; diff --git a/src/include/taler_merchant_pay_service.h b/src/include/taler_merchant_pay_service.h @@ -72,6 +72,9 @@ enum TALER_MERCHANT_OrderPayOptionType /** * Container describing *one* option for a payment request. + * + * Applications should typically not use this struct directly, + * only through respective TALER_MERCHANT_ORDER_PAY_OPTION_-macros */ struct TALER_MERCHANT_OrderPayOption { @@ -122,92 +125,298 @@ struct TALER_MERCHANT_OrderPayOption * Status codes returned from #TALER_MERCHANT_order_pay_set_options() and * #TALER_MERCHANT_order_pay_start(). */ -enum TALER_MERCHANT_OrderPayOptionErrorCode +enum TALER_MERCHANT_OrderPayErrorCode { TALER_MERCHANT_OPOEC_OK = 0, /**< everything fine */ TALER_MERCHANT_OPOEC_UNKNOWN_OPTION, /**< unrecognised option kind */ TALER_MERCHANT_OPOEC_DUPLICATE_OPTION, /**< option given more than once */ TALER_MERCHANT_OPOEC_INVALID_VALUE, /**< semantic/format error in value */ - TALER_MERCHANT_OPOEC_MISSING_MANDATORY /**< required field missing at start */ + TALER_MERCHANT_OPOEC_MISSING_MANDATORY, /**< required field missing at start */ + TALER_MERCHANT_OPOEC_URL_FAILURE, /**< failed to build request URL */ + TALER_MERCHANT_OPOEC_CURL_FAILURE /**< failed to init/schedule CURL */ }; + +/** + * Terminate the list of payment options. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_END \ } + +/** + * Specify the merchant’s URL for the payment request. + * + * @param _url NULL-terminated string holding the merchant endpoint. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL("https://shop.example/pay"), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL(_url) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL, \ .details.merchant_url = (_url) \ } + +/** + * Supply the session identifier for this payment. + * + * @param _sid session ID string. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID("ABC123"), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID(_sid) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_SESSION_ID, \ .details.session_id = (_sid) \ } + +/** + * Supply a private contract hash for the order. + * + * @param _h pointer to a TALER_PrivateContractHashP. + * + * @par Example + * \code + * struct TALER_PrivateContractHashP *h = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT(h), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT(_h) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_H_CONTRACT, \ .details.h_contract = (_h) \ } + +/** + * Supply which choice index the customer(wallet) selected. + * from contract terms. + * + * @param _idx choice index. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(2), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(_idx) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX, \ .details.choice_index = (_idx) \ } + +/** + * Specify the amount to be paid. + * + * @param _amt pointer to a TALER_Amount struct. + * + * @par Example + * \code + * struct TALER_Amount amt = ...; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(&amt), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(_amt) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_AMOUNT, \ .details.amount = *(_amt) \ } + +/** + * Set the maximum acceptable fee. + * + * @param _fee pointer to a TALER_Amount struct for the fee cap. + * + * @par Example + * \code + * struct TALER_Amount fee = ...; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(&fee), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(_fee) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_MAX_FEE, \ .details.max_fee = *(_fee) \ } + +/** + * Provide the merchant’s public key. + * + * @param _mpub pointer to a TALER_MerchantPublicKeyP. + * + * @par Example + * \code + * struct TALER_MerchantPublicKeyP *mp = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB(mp), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB(_mpub) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB, \ .details.merchant_pub = *(_mpub) \ } + +/** + * Stamp the request with a specific time. + * + * @param _ts GNUNET_TIME_Timestamp value. + * + * @par Example + * \code + * struct GNUNET_TIME_Timestamp now = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP(now), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP(_ts) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_TIMESTAMP, \ .details.timestamp = (_ts) \ } + +/** + * Set a deadline by which refunds may be issued. + * + * @param _ts GNUNET_TIME_Timestamp for the refund deadline. + * + * @par Example + * \code + * struct GNUNET_TIME_Timestamp rd = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(rd), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(_ts) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE, \ .details.refund_deadline = (_ts) \ } + +/** + * Set a deadline by which payment must be completed. + * + * @param _ts GNUNET_TIME_Timestamp for the payment deadline. + * + * @par Example + * \code + * struct GNUNET_TIME_Timestamp pd = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE(pd), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE(_ts) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE, \ .details.pay_deadline = (_ts) \ } + +/** + * Supply the merchant wire transaction hash. + * + * @param _hwire pointer to a TALER_MerchantWireHashP. + * + * @par Example + * \code + * struct TALER_MerchantWireHashP *wh = …; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE(wh), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE(_hwire) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_H_WIRE, \ .details.h_wire = *(_hwire) \ } + +/** + * Provide the unique order identifier. + * + * @param _oid NULL-terminated string of the order ID. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID("order-42"), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID(_oid) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_ORDER_ID, \ .details.order_id = (_oid) \ } + +/** + * Include a list of pay coins. + * + * @param _num number of coins in the array + * @param _coins pointer to array of TALER_MERCHANT_PayCoin. + * + * @par Example + * \code + * const struct TALER_MERCHANT_PayCoin coins[2] = { … }; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_COINS(2, coins), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_COINS(_num,_coins) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_COINS, \ @@ -215,6 +424,23 @@ enum TALER_MERCHANT_OrderPayOptionErrorCode .coins = (_coins) } \ } + +/** + * Include a list of input tokens. + * + * @param _num number of tokens + * @param _tokens pointer to array of TALER_MERCHANT_UseToken. + * + * @par Example + * \code + * const struct TALER_MERCHANT_UseToken toks[1] = { … }; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(1, toks), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ + #define TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(_num,_tokens) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS, \ @@ -222,6 +448,22 @@ enum TALER_MERCHANT_OrderPayOptionErrorCode .tokens = (_tokens) } \ } + +/** + * Include a list of output tokens. + * + * @param _num number of output tokens + * @param _otokens pointer to array of TALER_MERCHANT_OutputToken. + * + * @par Example + * \code + * const struct TALER_MERCHANT_OutputToken ots[3] = { … }; + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(3, ots), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(_num,_otokens) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS, \ @@ -229,24 +471,84 @@ enum TALER_MERCHANT_OrderPayOptionErrorCode .output_tokens = (_otokens) } \ } + +/** + * Supply the Donau service URL. + * + * @param _u NULL-terminated string of the Donau endpoint. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_URL("https://donau.example"), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_URL(_u) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_URL, \ .details.donau_url = (_u) \ } + +/** + * Specify the Donau “year” parameter. + * + * @param _y 64-bit unsigned integer for the Donau year. + * + * @par Example + * \code + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_YEAR(2025), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_YEAR(_y) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR, \ .details.donau_year = (_y) \ } + +/** + * Supply the Donau “budis” JSON structure. + * + * @param _js pointer to a json_t* holding the budis data. + * + * @par Example + * \code + * json_t *budis = json_pack("{s:i}", "quota", 100); + * struct TALER_MERCHANT_OrderPayOption opts[] = { + * TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_BUDIS(budis), + * TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() + * }; + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_BUDIS(_js) \ (struct TALER_MERCHANT_OrderPayOption){ \ .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS, \ .details.donau_budis_json = (_js) \ } + +/** + * Helper to call TALER_MERCHANT_order_pay_set_options() + * with a compound‐literal array terminated by TERMINATE(). + * + * @param ph the payment handle + * @param ... a comma-separated list of `_OPTION_*()` macros + * + * @par Example + * \code + * TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(handle, + * TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL("https://…"), + * TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(&amt), + * TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(&fee) + * ); + * \endcode + */ #define TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(ph,...) \ MHD_NOWARN_COMPOUND_LITERALS_ \ TALER_MERCHANT_order_pay_set_options ( \ @@ -255,6 +557,7 @@ enum TALER_MERCHANT_OrderPayOptionErrorCode {__VA_ARGS__, TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()}), \ MHD_OPTIONS_ARRAY_MAX_SIZE) \ + /** * @brief Create and initialize a new payment handle. * @@ -269,6 +572,7 @@ TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx, TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *pay_cb_cls); + /** * @brief Configure payment options on a handle. * @@ -279,12 +583,13 @@ TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx, * @return #TALER_MERCHANT_OPOEC_OK on success; * error code otherwise */ -enum TALER_MERCHANT_OrderPayOptionErrorCode +enum TALER_MERCHANT_OrderPayErrorCode TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, const struct TALER_MERCHANT_OrderPayOption options[], size_t max_options); + /** * @brief Start processing the payment request. * @@ -292,9 +597,10 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, * @return #TALER_MERCHANT_OPOEC_OK on successful dispatch; * error code on validation or dispatch failure */ -enum TALER_MERCHANT_OrderPayOptionErrorCode +enum TALER_MERCHANT_OrderPayErrorCode TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph); + /** * @brief Cancel an in-flight or pending payment. * @@ -302,5 +608,4 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph); */ void TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph); - #endif /* TALER_MERCHANT_PAY_SERVICE_H */ \ No newline at end of file diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h @@ -667,6 +667,8 @@ TALER_TESTING_cmd_merchant_post_orders3 ( * the proposal request. * @param http_status expected HTTP status. * @param token_family_slug slug of the token family to use + * @param choice_description description of the choice + * @param choice_description_i18n json of description translations * @param num_inputs number of input tokens. * @param num_outputs number of output tokens. * @param order_id the name of the order to add. diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -738,6 +738,19 @@ typedef void /** + * Typically called by `select_order_blinded_sigs` + * + * @param cls closure + * @param hash hash of the token + * @param blinded_sig blinded signature for the token + */ +typedef void +(*TALER_MERCHANTDB_BlindedSigCallback)( + void *cls, + struct GNUNET_HashCode *hash, + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig); + +/** * Function called with information about a coin that was deposited. * * @param cls closure @@ -2366,6 +2379,22 @@ struct TALER_MERCHANTDB_Plugin /** + * Insert blinded signatures for an order. + * + * @param cls closure + * @param order_id order ID to insert blinded signatures for + * @param hash hash of the token + * @param blinded_sigs JSON array of blinded signatures + */ + enum GNUNET_DB_QueryStatus + (*insert_order_blinded_sigs)( + void *cls, + const char *order_id, + const struct GNUNET_HashCode *hash, + const struct GNUNET_CRYPTO_BlindedSignature *blind_sig); + + + /** * Release an inventory lock by UUID. Releases ALL stocks locked under * the given UUID. * @@ -2405,15 +2434,15 @@ struct TALER_MERCHANTDB_Plugin * * @param cls closure * @param order_id order ID to select blinded signatures for - * @param blinded_sigs set to the JSON array of blinded signatures on success - * @param blind_sig_out set to the first blinded signature of blinded_sigs + * @param cb callback to call for each blinded signature found + * @param cb_cls closure for @a cb */ enum GNUNET_DB_QueryStatus (*select_order_blinded_sigs)( void *cls, const char *order_id, - json_t **blinded_sigs, - struct GNUNET_CRYPTO_BlindedSignature *blind_sig_out); + TALER_MERCHANTDB_BlindedSigCallback cb, + void *cb_cls); /** @@ -4187,20 +4216,6 @@ struct TALER_MERCHANTDB_Plugin ); /** - * Insert blinded signatures for an order. - * - * @param cls closure - * @param order_id order ID to insert blinded signatures for - * @param blinded_sigs JSON array of blinded signatures - */ - enum GNUNET_DB_QueryStatus - (*insert_order_blinded_sigs)( - void *cls, - const char *order_id, - const json_t *blinded_sigs, - const struct GNUNET_CRYPTO_BlindedSignature *blind_sig); - - /** * Select donau instance by serial number. * * @param cls closure @@ -4225,7 +4240,7 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*select_donau_instances)( void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *id, TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls); @@ -4253,7 +4268,7 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*delete_donau_instance)( void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *id, const uint64_t charity_id ); diff --git a/src/lib/merchant_api_get_donau_instance.c b/src/lib/merchant_api_get_donau_instance.c @@ -99,6 +99,8 @@ parse_donau_instances (const json_t *ia, struct DONAU_Keys *donau_keys_ptr; struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("donau_instance_serial", + &instance->donau_instance_serial), GNUNET_JSON_spec_string ("donau_url", &instance->donau_url), GNUNET_JSON_spec_string ("charity_name", diff --git a/src/lib/merchant_api_post_donau_instance.c b/src/lib/merchant_api_post_donau_instance.c @@ -172,18 +172,8 @@ TALER_MERCHANT_donau_instances_post ( req_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("donau_url", charity->charity_url), - GNUNET_JSON_pack_string ("charity_name", - charity->name), - GNUNET_JSON_pack_data_auto ("charity_pub_key", - &charity->charity_pub), GNUNET_JSON_pack_uint64 ("charity_id", - charity->charity_id), - TALER_JSON_pack_amount ("max_per_year", - &charity->max_per_year), - TALER_JSON_pack_amount ("receipts_to_date", - &charity->receipts_to_date), - GNUNET_JSON_pack_uint64 ("current_year", - charity->current_year) + charity->charity_id) ); dph = GNUNET_new (struct TALER_MERCHANT_DonauInstancePostHandle); diff --git a/src/lib/taler_merchant_pay_service.c b/src/lib/taler_merchant_pay_service.c @@ -609,7 +609,7 @@ TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph) * @param snippet JSON object representing the option payload * @return #TALER_MERCHANT_OPOEC_OK if stored; appropriate error code otherwise */ -static enum TALER_MERCHANT_OrderPayOptionErrorCode +static enum TALER_MERCHANT_OrderPayErrorCode store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph, enum TALER_MERCHANT_OrderPayOptionType ot, json_t *snippet) @@ -634,7 +634,7 @@ store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph, * entries, validates and stores each into @a ph. Handles JSON packing and * internal state updates for coins, tokens, deadlines, Donau data, etc. */ -enum TALER_MERCHANT_OrderPayOptionErrorCode +enum TALER_MERCHANT_OrderPayErrorCode TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, const struct TALER_MERCHANT_OrderPayOption options[], @@ -657,7 +657,7 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, json_t *js = GNUNET_JSON_PACK (GNUNET_JSON_pack_string ("session_id", o->details. session_id)); - enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + enum TALER_MERCHANT_OrderPayErrorCode ec = store_json_option (ph, o->ot, js); @@ -818,7 +818,7 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, * and issues an asynchronous HTTP POST. The payment handle's callback * will receive completion notifications. */ -enum TALER_MERCHANT_OrderPayOptionErrorCode +enum TALER_MERCHANT_OrderPayErrorCode TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) { /* all the old mandatory checks */ @@ -954,11 +954,12 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) /* Putting coins to the body*/ { - enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + enum TALER_MERCHANT_OrderPayErrorCode ec = store_json_option (ph, TALER_MERCHANT_OrderPayOptionType_COINS, GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("coins", arr) + GNUNET_JSON_pack_array_steal ("coins", + arr) )); if (TALER_MERCHANT_OPOEC_OK != ec) { @@ -1006,7 +1007,8 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) store_json_option (ph, TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS, GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("tokens", arr) + GNUNET_JSON_pack_array_steal ("tokens", + arr) ) ); } @@ -1030,7 +1032,7 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) "Could not construct request URL.\n"); json_decref (ph->body); GNUNET_free (ph); - return TALER_MERCHANT_OPOEC_INVALID_VALUE; + return TALER_MERCHANT_OPOEC_URL_FAILURE; } eh = TALER_MERCHANT_curl_easy_get_ (ph->url); @@ -1043,7 +1045,7 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) curl_easy_cleanup (eh); GNUNET_free (ph->url); GNUNET_free (ph); - return TALER_MERCHANT_OPOEC_INVALID_VALUE; + return TALER_MERCHANT_OPOEC_CURL_FAILURE; } ph->job = GNUNET_CURL_job_add2 (ph->ctx, diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c @@ -389,8 +389,6 @@ struct PayState /** * Number of donau_tokens in @e issued_tokens. - * - * For this moment, it can only be 0 or 1. */ unsigned int num_donau_tokens; @@ -1123,7 +1121,7 @@ pay_run (void *cls, TALER_TESTING_FAIL (is); return; } - ps->num_donau_tokens++; + ps->num_donau_tokens = ps->donau_data.num_bkps; } #else /* HAVE_DONAU_DONAU_SERVICE_H */ /* SIMPLY NOTHING */