merchant

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

commit 60c5ee76aebfda9c378a4d9fe81544ba0ec5b214
parent 9e8f9c9d4b8febe029594ab19ac2d239154fd342
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Wed,  8 Feb 2017 16:13:13 +0100

Fixing testcases according to the renaming occurred
in the protocol.  Please note, comments do NOT match
code, /proposal uses the POST method, and tracking
API are NOT tested yet.

Diffstat:
Msrc/backend/taler-merchant-httpd.c | 6+++---
Msrc/backend/taler-merchant-httpd_pay.c | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/backend/taler-merchant-httpd_proposal.c | 62++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/backend/taler-merchant-httpd_track-transaction.c | 40+++++++++++++++++++++++++++++-----------
Msrc/backend/taler-merchant-httpd_track-transfer.c | 2+-
Msrc/backenddb/plugin_merchantdb_postgres.c | 27++++++++++++++++++++++-----
Msrc/backenddb/test_merchantdb.c | 2+-
Msrc/include/taler_merchant_service.h | 4+---
Msrc/lib/merchant_api_pay.c | 12++++--------
Msrc/lib/merchant_api_proposal.c | 10+++++-----
Msrc/lib/merchant_api_track_transaction.c | 11+++++++----
Msrc/lib/test_merchant_api.c | 228++++++++++++++++++++++++++++++++++++-------------------------------------------
12 files changed, 288 insertions(+), 192 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -164,7 +164,7 @@ url_handler (void *cls, { "/", MHD_HTTP_METHOD_GET, "text/plain", "Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0, &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - { "/proposal", MHD_HTTP_METHOD_PUT, "application/json", + { "/proposal", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, &MH_handler_proposal_put, MHD_HTTP_OK }, { "/pay", MHD_HTTP_METHOD_POST, "application/json", @@ -192,7 +192,7 @@ url_handler (void *cls, "Only GET is allowed", 0, &MH_handler_proposal_lookup, MHD_HTTP_OK}, { "/proposal", NULL, "text/plain", - "Only GET/PUT are allowed", 0, + "Only GET/POST are allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, {NULL, NULL, NULL, NULL, 0, 0 } @@ -723,7 +723,7 @@ run (void *cls, NULL); GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-merchant-httpd", - "INFO", + "DEBUG", NULL)); if (GNUNET_SYSERR == TMH_EXCHANGES_init (config)) diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c @@ -159,7 +159,7 @@ struct PayContext /** * Transaction ID given in @e root. */ - const char *transaction_id; + const char *order_id; /** * Maximum fee the merchant is willing to pay, from @e root. @@ -395,9 +395,13 @@ deposit_cb (void *cls, return; } /* store result to DB */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing successful payment for h_proposal_data '%s'\n", + GNUNET_h2s (&pc->h_proposal_data)); + if (GNUNET_OK != db->store_deposit (db->cls, - pc->transaction_id, + &pc->h_proposal_data, &pc->mi->pubkey, &dc->coin_pub, &dc->amount_with_fee, @@ -430,9 +434,9 @@ deposit_cb (void *cls, resume_pay_with_response (pc, MHD_HTTP_OK, TMH_RESPONSE_make_json_pack ("{s:s, s:o}", - "merchant_sig", + "sig", json_string_value (GNUNET_JSON_from_data_auto (&sig)), - "hash", + "h_proposal_data", GNUNET_JSON_from_data (&pc->h_proposal_data, sizeof (struct GNUNET_HashCode)))); } @@ -854,6 +858,33 @@ get_instance (struct json_t *json); /** + * Just a stub used to double-check if a transaction + * has been correctly inserted into db. + * + * @param cls closure + * @param transaction_id of the contract + * @param merchant's public key + * @param exchange_uri URI of the exchange + * @param h_contract hash of the contract + * @param h_wire hash of our wire details + * @param timestamp time of the confirmation + * @param refund refund deadline + * @param total_amount total amount we receive for the contract after fees + */ +static void +transaction_double_check (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const char *exchange_uri, + const struct GNUNET_HashCode *h_proposal_data, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund, + const struct TALER_Amount *total_amount) +{ + return; +} + +/** * Accomplish this payment. * * @param rh context of the handler @@ -945,14 +976,13 @@ MH_handler_pay (struct TMH_RequestHandler *rh, struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount ("amount", &pc->amount), GNUNET_JSON_spec_json ("coins", &coins), - GNUNET_JSON_spec_fixed_auto ("H_contract", &pc->h_proposal_data), + GNUNET_JSON_spec_fixed_auto ("h_proposal_data", &pc->h_proposal_data), TALER_JSON_spec_amount ("max_fee", &pc->max_fee), GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig), GNUNET_JSON_spec_string ("exchange", &chosen_exchange), GNUNET_JSON_spec_absolute_time ("refund_deadline", &pc->refund_deadline), GNUNET_JSON_spec_absolute_time ("pay_deadline", &pc->pay_deadline), GNUNET_JSON_spec_absolute_time ("timestamp", &pc->timestamp), - GNUNET_JSON_spec_string ("transaction_id", &pc->transaction_id), GNUNET_JSON_spec_end() }; @@ -990,6 +1020,27 @@ MH_handler_pay (struct TMH_RequestHandler *rh, pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); pdps.purpose.size = htonl (sizeof (pdps)); pdps.hash = pc->h_proposal_data; + + struct GNUNET_HashCode dummy; + + GNUNET_CRYPTO_hash (&merchant_sig.eddsa_sig, + sizeof (merchant_sig.eddsa_sig), + &dummy); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Verifying signature '%s'\n", + GNUNET_h2s (&dummy)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "of hashed data '%s'\n", + GNUNET_h2s (&pc->h_proposal_data)); + + GNUNET_CRYPTO_hash (&pc->mi->privkey.eddsa_priv, + sizeof (pc->mi->privkey.eddsa_priv), + &dummy); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "with private key '%s'\n", + GNUNET_h2s (&dummy)); + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_CONTRACT, &pdps.purpose, @@ -1159,7 +1210,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, } if (GNUNET_NO == pc->transaction_exits) { - /* #4521 goes here: Check if the customer respects pay_deadline */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dealing with new transaction '%s'\n", + GNUNET_h2s (&pc->h_proposal_data)); now = GNUNET_TIME_absolute_get (); if (now.abs_value_us > pc->pay_deadline.abs_value_us) { @@ -1176,6 +1229,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, "The time to pay for this contract has expired."); } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing transaction '%s'\n", + GNUNET_h2s (&pc->h_proposal_data)); if (GNUNET_OK != db->store_transaction (db->cls, &pc->h_proposal_data, @@ -1192,6 +1248,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR, "Merchant database error"); } + if (GNUNET_OK != db->find_transaction (db->cls, + &pc->h_proposal_data, + &pc->mi->pubkey, + &transaction_double_check, + NULL)) + GNUNET_break (0); } MHD_suspend_connection (connection); diff --git a/src/backend/taler-merchant-httpd_proposal.c b/src/backend/taler-merchant-httpd_proposal.c @@ -148,8 +148,8 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_EddsaSignature merchant_sig; struct TALER_Amount total; struct TALER_Amount max_fee; - const char *transaction_id; - struct GNUNET_HashCode h_tid; + const char *order_id; + struct GNUNET_HashCode h_oid; json_t *products; json_t *merchant; struct GNUNET_TIME_Absolute timestamp; @@ -158,7 +158,7 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh, struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount ("amount", &total), TALER_JSON_spec_amount ("max_fee", &max_fee), - GNUNET_JSON_spec_string ("transaction_id", &transaction_id), + GNUNET_JSON_spec_string ("order_id", &order_id), /* The following entries we don't actually need, except to check that the order is well-formed */ GNUNET_JSON_spec_json ("products", &products), @@ -259,17 +259,39 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh, GNUNET_assert (GNUNET_OK == TALER_JSON_hash (order, &pdps.hash)); + + /*FIXME: do NOT keep in production, private key logged!*/ + struct GNUNET_HashCode dummy; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing h_proposal_data '%s'\n", + GNUNET_h2s (&pdps.hash)); + + GNUNET_CRYPTO_hash (&mi->privkey.eddsa_priv, + sizeof (mi->privkey.eddsa_priv), + &dummy); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "with private key '%s'\n", + GNUNET_h2s (&dummy)); + GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, &pdps.purpose, &merchant_sig); - - GNUNET_CRYPTO_hash (transaction_id, - strlen (transaction_id), - &h_tid); + GNUNET_CRYPTO_hash (&merchant_sig, + sizeof (merchant_sig), + &dummy); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "generating signature '%s'\n", + GNUNET_h2s (&dummy)); + + GNUNET_CRYPTO_hash (order_id, + strlen (order_id), + &h_oid); if (GNUNET_OK != db->insert_proposal_data (db->cls, - &h_tid, + &h_oid, order)) return TMH_RESPONSE_reply_internal_error (connection, TALER_EC_PROPOSAL_STORE_DB_ERROR, @@ -280,7 +302,7 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh, MHD_HTTP_OK, "{s:O, s:o s:o}", "data", order, - "merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig), + "sig", GNUNET_JSON_from_data_auto (&merchant_sig), "hash", GNUNET_JSON_from_data_auto (&pdps.hash)); GNUNET_JSON_parse_free (spec); json_decref (root); @@ -306,25 +328,25 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - const char *transaction_id; - struct GNUNET_HashCode h_tid; + const char *order_id; + struct GNUNET_HashCode h_oid; int res; json_t *proposal_data; - transaction_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "transaction_id"); - if (NULL == transaction_id) + order_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "order_id"); + if (NULL == order_id) return TMH_RESPONSE_reply_arg_missing (connection, TALER_EC_PARAMETER_MISSING, - "transaction_id"); - GNUNET_CRYPTO_hash (transaction_id, - strlen (transaction_id), - &h_tid); + "order_id"); + GNUNET_CRYPTO_hash (order_id, + strlen (order_id), + &h_oid); res = db->find_proposal_data (db->cls, &proposal_data, - &h_tid); + &h_oid); if (GNUNET_NO == res) return TMH_RESPONSE_reply_not_found (connection, TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND, diff --git a/src/backend/taler-merchant-httpd_track-transaction.c b/src/backend/taler-merchant-httpd_track-transaction.c @@ -844,11 +844,13 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, size_t *upload_data_size) { struct TrackTransactionContext *tctx; - const char *h_proposal_data_str; + const char *order_id; const char *instance; int ret; struct GNUNET_HashCode h_instance; + struct GNUNET_HashCode h_order_id; struct GNUNET_HashCode h_proposal_data; + struct json_t *proposal_data; if (NULL == *connection_cls) { @@ -893,13 +895,13 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, "Not sure why we are here, should be suspended\n"); return MHD_YES; /* still work in progress */ } - h_proposal_data_str = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "hash"); - if (NULL == h_proposal_data_str) + order_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "order_id"); + if (NULL == order_id) return TMH_RESPONSE_reply_arg_missing (connection, TALER_EC_PARAMETER_MISSING, - "hash"); + "order_id"); instance = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "instance"); @@ -908,9 +910,11 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, GNUNET_CRYPTO_hash (instance, strlen (instance), &h_instance); - GNUNET_CRYPTO_hash (h_proposal_data_str, - strlen (h_proposal_data_str), - &h_proposal_data); + GNUNET_CRYPTO_hash (order_id, + strlen (order_id), + &h_order_id); + + tctx->mi = GNUNET_CONTAINER_multihashmap_get (by_id_map, &h_instance); @@ -918,6 +922,21 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, return TMH_RESPONSE_reply_not_found (connection, TALER_EC_TRACK_TRANSACTION_INSTANCE_UNKNOWN, "unknown instance"); + + if (GNUNET_YES != db->find_proposal_data (db->cls, + &proposal_data, + &h_order_id)) + + return TMH_RESPONSE_reply_not_found (connection, + TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND, + "Given order_id doesn't map to any proposal"); + TALER_JSON_hash (proposal_data, + &h_proposal_data); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to track h_proposal_data '%s'\n", + GNUNET_h2s (&h_proposal_data)); + ret = db->find_transaction (db->cls, &h_proposal_data, &tctx->mi->pubkey, @@ -927,7 +946,7 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, { return TMH_RESPONSE_reply_not_found (connection, TALER_EC_TRACK_TRANSACTION_TRANSACTION_UNKNOWN, - "id"); + "h_proposal_data is unknown"); } if ( (GNUNET_SYSERR == ret) || (0 != memcmp (&tctx->h_proposal_data, @@ -974,5 +993,4 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, return MHD_YES; } - /* end of taler-merchant-httpd_track-transaction.c */ diff --git a/src/backend/taler-merchant-httpd_track-transfer.c b/src/backend/taler-merchant-httpd_track-transfer.c @@ -175,7 +175,7 @@ resume_track_transfer_with_response (struct TrackTransferContext *rctx, rctx->response_code = response_code; rctx->response = response; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /track/transaction handling as exchange interaction is done (%u)\n", + "Resuming /track/transfer handling as exchange interaction is done (%u)\n", response_code); if (NULL != rctx->timeout_task) { diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -145,7 +145,7 @@ postgres_drop_tables (void *cls) PG_EXEC_INDEX (pg, "DROP TABLE merchant_deposits;"); PG_EXEC_INDEX (pg, "DROP TABLE merchant_transactions;"); PG_EXEC_INDEX (pg, "DROP TABLE merchant_proofs;"); - PG_EXEC_INDEX (pg, "DROP TABLE merchant_contract_maps;"); + PG_EXEC_INDEX (pg, "DROP TABLE merchant_proposal_data;"); return GNUNET_OK; } @@ -164,9 +164,9 @@ postgres_initialize (void *cls) /* Setup tables */ PG_EXEC (pg, "CREATE TABLE IF NOT EXISTS merchant_proposal_data (" - "h_proposal_data BYTEA NOT NULL CHECK (LENGTH(h_proposal_data)=64)" + "h_order_id BYTEA NOT NULL" ",proposal_data BYTEA NOT NULL" - ",PRIMARY KEY (h_proposal_data)" + ",PRIMARY KEY (h_order_id)" ");"); PG_EXEC (pg, @@ -278,7 +278,7 @@ postgres_initialize (void *cls) PG_PREPARE (pg, "insert_proposal_data", "INSERT INTO merchant_proposal_data" - "(h_transaction_id" + "(h_order_id" ",proposal_data)" " VALUES " "($1, $2)", @@ -288,7 +288,7 @@ postgres_initialize (void *cls) "find_proposal_data", "SELECT proposal_data FROM merchant_proposal_data" " WHERE" - " h_transaction_id=$1", + " h_order_id=$1", 1); PG_PREPARE (pg, @@ -538,6 +538,9 @@ postgres_store_transaction (void *cls, GNUNET_PQ_query_param_end }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing transaction with h_proposal_data '%s'\n", + GNUNET_h2s (h_proposal_data)); result = GNUNET_PQ_exec_prepared (pg->conn, "insert_transaction", @@ -594,6 +597,9 @@ postgres_store_deposit (void *cls, GNUNET_PQ_query_param_end }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "storing payment for h_proposal_data '%s'\n", + GNUNET_h2s (h_proposal_data)); result = GNUNET_PQ_exec_prepared (pg->conn, "insert_deposit", params); @@ -821,6 +827,10 @@ postgres_find_transaction (void *cls, GNUNET_PQ_query_param_end }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finding transaction for h_proposal_data '%s'\n", + GNUNET_h2s (h_proposal_data)); + result = GNUNET_PQ_exec_prepared (pg->conn, "find_transaction", params); @@ -832,6 +842,10 @@ postgres_find_transaction (void *cls, } if (0 == PQntuples (result)) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Could NOT find transaction for h_proposal_data '%s'\n", + GNUNET_h2s (h_proposal_data)); + PQclear (result); return GNUNET_NO; } @@ -913,6 +927,9 @@ postgres_find_payments (void *cls, GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "finding payment for h_proposal_data '%s'\n", + GNUNET_h2s (h_proposal_data)); result = GNUNET_PQ_exec_prepared (pg->conn, "find_deposits", params); diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c @@ -476,7 +476,7 @@ main (int argc, GNUNET_break (0); return -1; } - GNUNET_log_setup (argv[0], "INFO", NULL); + GNUNET_log_setup (argv[0], "DEBUG", NULL); plugin_name++; (void) GNUNET_asprintf (&testname, "test-merchantdb-%s", diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h @@ -238,7 +238,6 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx, const char *merchant_uri, const char *instance, const struct GNUNET_HashCode *h_contract, - uint64_t transaction_id, const struct TALER_Amount *amount, const struct TALER_Amount *max_fee, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -331,7 +330,6 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx, const struct GNUNET_HashCode *h_contract, const struct TALER_Amount *amount, const struct TALER_Amount *max_fee, - uint64_t transaction_id, const struct TALER_MerchantSignatureP *merchant_sig, struct GNUNET_TIME_Absolute refund_deadline, struct GNUNET_TIME_Absolute pay_deadline, @@ -520,7 +518,7 @@ struct TALER_MERCHANT_TrackTransactionHandle * TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx, const char *backend_uri, const char *instance, - uint64_t transaction_id, + const char *order_id, TALER_MERCHANT_TrackTransactionCallback track_transaction_cb, void *track_transaction_cb_cls); diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c @@ -279,7 +279,6 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx, const char *merchant_uri, const char *instance, const struct GNUNET_HashCode *h_proposal_data, - uint64_t transaction_id, const struct TALER_Amount *amount, const struct TALER_Amount *max_fee, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -348,7 +347,6 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx, h_proposal_data, amount, max_fee, - transaction_id, merchant_sig, refund_deadline, pay_deadline, @@ -391,10 +389,9 @@ struct TALER_MERCHANT_Pay * TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx, const char *merchant_uri, const char *instance, - const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_proposal_data, const struct TALER_Amount *amount, const struct TALER_Amount *max_fee, - uint64_t transaction_id, const struct TALER_MerchantSignatureP *merchant_sig, struct GNUNET_TIME_Absolute refund_deadline, struct GNUNET_TIME_Absolute pay_deadline, @@ -560,14 +557,13 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx, } } /* end of sanity check */ - pay_obj = json_pack ("{s:o," /* H_contract */ - " s:I, s:o," /* transaction id, timestamp */ + pay_obj = json_pack ("{s:o," /* h_proposal_data */ + " s:o," /* timestamp */ " s:o, s:o," /* refund_deadline, pay_deadline */ " s:s," /* exchange */ " s:o, s:o," /* coins, max_fee */ " s:o, s:o}",/* amount, signature */ - "H_contract", GNUNET_JSON_from_data_auto (h_contract), - "transaction_id", (json_int_t) transaction_id, + "h_proposal_data", GNUNET_JSON_from_data_auto (h_proposal_data), "timestamp", GNUNET_JSON_from_time_abs (timestamp), "refund_deadline", GNUNET_JSON_from_time_abs (refund_deadline), "pay_deadline", GNUNET_JSON_from_time_abs (pay_deadline), diff --git a/src/lib/merchant_api_proposal.c b/src/lib/merchant_api_proposal.c @@ -132,8 +132,8 @@ handle_proposal_finished (void *cls, case MHD_HTTP_OK: { struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("proposal_data", &proposal_data), - GNUNET_JSON_spec_fixed_auto ("merchant_sig", &sig), + GNUNET_JSON_spec_json ("data", &proposal_data), + GNUNET_JSON_spec_fixed_auto ("sig", &sig), GNUNET_JSON_spec_fixed_auto ("hash", &hash), GNUNET_JSON_spec_end() }; @@ -287,7 +287,7 @@ handle_proposal_lookup_finished (void *cls, struct TALER_MERCHANT_ProposalLookupOperation * TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, const char *backend_uri, - const char *transaction_id, + const char *order_id, TALER_MERCHANT_ProposalLookupOperationCallback plo_cb, void *plo_cb_cls) { @@ -300,9 +300,9 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, plo->cb_cls = plo_cb_cls; GNUNET_asprintf (&plo->url, - "%s/proposal?transaction_id=%s", + "%s/proposal?order_id=%s", backend_uri, - transaction_id); + order_id); eh = curl_easy_init (); if (CURLE_OK != curl_easy_setopt (eh, CURLOPT_URL, diff --git a/src/lib/merchant_api_track_transaction.c b/src/lib/merchant_api_track_transaction.c @@ -201,7 +201,7 @@ handle_track_transaction_finished (void *cls, /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "track transaction URI not found\n"); + "Did not find any data\n"); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API @@ -240,7 +240,7 @@ struct TALER_MERCHANT_TrackTransactionHandle * TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx, const char *backend_uri, const char *instance, - uint64_t transaction_id, + const char *order_id, TALER_MERCHANT_TrackTransactionCallback track_transaction_cb, void *track_transaction_cb_cls) { @@ -252,10 +252,13 @@ TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx, tdo->cb = track_transaction_cb; tdo->cb_cls = track_transaction_cb_cls; GNUNET_asprintf (&tdo->url, - "%s/track/transaction?id=%llu&instance=%s", + "%s/track/transaction?order_id=%s&instance=%s", backend_uri, - (unsigned long long) transaction_id, + order_id, instance); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URI '%s'\n", + tdo->url); eh = curl_easy_init (); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c @@ -479,9 +479,9 @@ struct Command struct TALER_MERCHANT_Pay *ph; /** - * Set to the transaction ID of the respective contract. + * Hashcode of the proposal data associated to this payment. */ - uint64_t transaction_id; + struct GNUNET_HashCode h_proposal_data; /** * Merchant's public key @@ -538,6 +538,9 @@ struct Command /** * #OC_PAY command which we expect in the result. + * Since we are tracking a bank transaction, we want to know + * which (Taler) deposit is associated with the bank + * transaction being tracked now. */ char *expected_pay_ref; @@ -1078,7 +1081,7 @@ pay_cb (void *cls, struct Command *cmd = &is->commands[is->ip]; struct PaymentResponsePS mr; struct GNUNET_CRYPTO_EddsaSignature sig; - struct GNUNET_HashCode h_contract; + struct GNUNET_HashCode h_proposal_data; const char *error_name; unsigned int error_line; @@ -1097,8 +1100,8 @@ pay_cb (void *cls, { /* Check signature */ struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("merchant_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract", &h_contract), + GNUNET_JSON_spec_fixed_auto ("sig", &sig), + GNUNET_JSON_spec_fixed_auto ("h_proposal_data", &h_proposal_data), GNUNET_JSON_spec_end () }; if (GNUNET_OK != @@ -1117,7 +1120,7 @@ pay_cb (void *cls, } mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK); mr.purpose.size = htonl (sizeof (mr)); - mr.h_contract = h_contract; + mr.h_proposal_data = h_proposal_data; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK, &mr.purpose, @@ -1208,18 +1211,31 @@ track_transfer_cb (void *cls, unsigned int i; int found; + /** + * Retrieve the deposit operation that is supposed + * to have been paid by the wtid used in this operation. + * After that, check if that operation is actually mentioned + * in the returned data. + */ ref = find_command (is, cmd->details.track_transfer.expected_pay_ref); GNUNET_assert (NULL != ref); found = GNUNET_NO; + + /** + * Iterating over the details makes little sense now, + * as each payment involves exatcly one coin. + */ for (i=0;i<details_length;i++) { struct TALER_Amount amount_with_fee; struct TALER_Amount amount_without_fee; struct TALER_Amount deposit_fee; const struct Command *cref; + const struct Command *proposal_ref; struct TALER_CoinSpendPublicKeyP coin_pub; + /* Extract */ GNUNET_assert (GNUNET_OK == TALER_string_to_amount (ref->details.pay.amount_without_fee, &amount_without_fee)); @@ -1231,20 +1247,28 @@ track_transfer_cb (void *cls, &amount_with_fee, &amount_without_fee)); + /* Find coin ('s public key) associated with the retrieved + deposit. Yes, one deposit - one coin. */ cref = find_command (is, ref->details.pay.coin_ref); + proposal_ref = find_command (is, + ref->details.pay.contract_ref); GNUNET_assert (NULL != cref); + GNUNET_assert (NULL != proposal_ref); switch (cref->oc) { case OC_WITHDRAW_SIGN: - GNUNET_CRYPTO_eddsa_key_get_public (&cref->details.reserve_withdraw.coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_get_public + (&cref->details.reserve_withdraw.coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); break; default: GNUNET_assert (0); } - if ( (details[i].transaction_id == ref->details.pay.transaction_id) && + if ( (0 == memcmp (&details[i].h_proposal_data, + &proposal_ref->details.proposal.hash, + sizeof (struct GNUNET_HashCode))) && (0 == TALER_amount_cmp (&details[i].coin_value, &amount_with_fee)) && (0 == TALER_amount_cmp (&details[i].coin_fee, @@ -1477,42 +1501,44 @@ interpreter_run (void *cls) cmd->oc); switch (cmd->oc) { - case OC_END: - result = GNUNET_OK; - if (instance_idx + 1 == ninstances) - { - GNUNET_SCHEDULER_shutdown (); + case OC_END: + result = GNUNET_OK; + if (instance_idx + 1 == ninstances) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + is->ip = 0; + instance_idx++; + instance = instances[instance_idx]; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Switching instance: '%s'\n", + instance); + + is->task = GNUNET_SCHEDULER_add_now (interpreter_run, + is); return; - } - is->ip = 0; - instance_idx++; - instance = instances[instance_idx]; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Switching instance: '%s'\n", - instance); - is->task = GNUNET_SCHEDULER_add_now (interpreter_run, - is); - return; - - case OC_PROPOSAL_LOOKUP: - { - const char *transaction_id; - - GNUNET_assert (NULL != cmd->details.proposal_lookup.proposal_reference); - ref = find_command (is, cmd->details.proposal_lookup.proposal_reference); - GNUNET_assert (NULL != ref); - - transaction_id = json_string_value (json_object_get (ref->details.proposal.proposal_data, "transaction_id")); - GNUNET_assert (NULL != - (cmd->details.proposal_lookup.plo - = TALER_MERCHANT_proposal_lookup (ctx, - MERCHANT_URI, - transaction_id, - proposal_lookup_cb, - is))); + case OC_PROPOSAL_LOOKUP: + { + const char *order_id; + + GNUNET_assert (NULL != cmd->details.proposal_lookup.proposal_reference); + ref = find_command (is, cmd->details.proposal_lookup.proposal_reference); + GNUNET_assert (NULL != ref); + + order_id = + json_string_value (json_object_get (ref->details.proposal.proposal_data, + "order_id")); + GNUNET_assert (NULL != + (cmd->details.proposal_lookup.plo + = TALER_MERCHANT_proposal_lookup (ctx, + MERCHANT_URI, + order_id, + proposal_lookup_cb, + is))); + } - } return; case OC_ADMIN_ADD_INCOMING: @@ -1719,7 +1745,7 @@ interpreter_run (void *cls) case OC_PAY: { struct TALER_MERCHANT_PayCoin pc; - uint64_t transaction_id; + const char *order_id; struct GNUNET_TIME_Absolute refund_deadline; struct GNUNET_TIME_Absolute pay_deadline; struct GNUNET_TIME_Absolute timestamp; @@ -1731,16 +1757,19 @@ interpreter_run (void *cls) const char *error_name; unsigned int error_line; - /* get amount */ + /* get proposal */ ref = find_command (is, cmd->details.pay.contract_ref); GNUNET_assert (NULL != ref); merchant_sig = ref->details.proposal.merchant_sig; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Depositing I on '%s'\n", + GNUNET_h2s (&ref->details.proposal.hash)); GNUNET_assert (NULL != ref->details.proposal.proposal_data); { /* Get information that need to be replied in the deposit permission */ struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("transaction_id", &transaction_id), + GNUNET_JSON_spec_string ("order_id", &order_id), GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline), GNUNET_JSON_spec_absolute_time ("pay_deadline", &pay_deadline), GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp), @@ -1765,23 +1794,22 @@ interpreter_run (void *cls) fail (is); return; } - cmd->details.pay.transaction_id = transaction_id; cmd->details.pay.merchant_pub = merchant_pub; } - /* initialize 'pc' (FIXME: to do in a loop later...) */ { + const struct Command *coin_ref; memset (&pc, 0, sizeof (pc)); - ref = find_command (is, - cmd->details.pay.coin_ref); + coin_ref = find_command (is, + cmd->details.pay.coin_ref); GNUNET_assert (NULL != ref); - switch (ref->oc) + switch (coin_ref->oc) { case OC_WITHDRAW_SIGN: - pc.coin_priv = ref->details.reserve_withdraw.coin_priv; - pc.denom_pub = ref->details.reserve_withdraw.pk->key; - pc.denom_sig = ref->details.reserve_withdraw.sig; - pc.denom_value = ref->details.reserve_withdraw.pk->value; + pc.coin_priv = coin_ref->details.reserve_withdraw.coin_priv; + pc.denom_pub = coin_ref->details.reserve_withdraw.pk->key; + pc.denom_sig = coin_ref->details.reserve_withdraw.sig; + pc.denom_value = coin_ref->details.reserve_withdraw.pk->value; break; default: GNUNET_assert (0); @@ -1811,12 +1839,16 @@ interpreter_run (void *cls) return; } } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Depositing II on '%s'\n", + GNUNET_h2s (&ref->details.proposal.hash)); + cmd->details.pay.ph = TALER_MERCHANT_pay_wallet (ctx, MERCHANT_URI, instance, &ref->details.proposal.hash, - transaction_id, &total_amount, &max_fee, &merchant_pub, @@ -1917,17 +1949,25 @@ interpreter_run (void *cls) is); return; case OC_TRACK_TRANSACTION: + { + const struct Command *proposal_ref; + const char *order_id; + ref = find_command (is, cmd->details.track_transaction.pay_ref); GNUNET_assert (NULL != ref); - /*FIXME check/assert return code */ + proposal_ref = find_command (is, + ref->details.pay.contract_ref); + order_id = json_string_value (json_object_get (proposal_ref->details.proposal.proposal_data, + "order_id")); cmd->details.track_transaction.tth = TALER_MERCHANT_track_transaction (ctx, MERCHANT_URI, - instance, /* got it NULL, right now */ - ref->details.pay.transaction_id, + instance, + order_id, &track_transaction_cb, is); + } return; case OC_HISTORY: @@ -2246,7 +2286,7 @@ run (void *cls) .details.proposal.order = "{\ \"max_fee\":\ {\"currency\":\"EUR\", \"value\":0, \"fraction\":50000000},\ - \"transaction_id\":1,\ + \"order_id\":\"1\",\ \"timestamp\":\"\\/Date(42)\\/\",\ \"refund_deadline\":\"\\/Date(0)\\/\",\ \"pay_deadline\":\"\\/Date(9999999999)\\/\",\ @@ -2257,7 +2297,7 @@ run (void *cls) { .oc = OC_PAY, .label = "deposit-simple", .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-contract-1", + .details.pay.contract_ref = "create-proposal-1", .details.pay.coin_ref = "withdraw-coin-1", .details.pay.amount_with_fee = "EUR:5", .details.pay.amount_without_fee = "EUR:4.99" }, @@ -2270,7 +2310,7 @@ run (void *cls) .details.proposal.order = "{\ \"max_fee\":\ {\"currency\":\"EUR\", \"value\":0, \"fraction\":50000000},\ - \"transaction_id\":2,\ + \"order_id\":\"2\",\ \"timestamp\":\"\\/Date(42)\\/\",\ \"refund_deadline\":\"\\/Date(0)\\/\",\ \"pay_deadline\":\"\\/Date(9999999999)\\/\",\ @@ -2283,7 +2323,7 @@ run (void *cls) { .oc = OC_PAY, .label = "deposit-double-2", .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.pay.contract_ref = "create-contract-2", + .details.pay.contract_ref = "create-proposal-2", .details.pay.coin_ref = "withdraw-coin-1", .details.pay.amount_with_fee = "EUR:5", .details.pay.amount_without_fee = "EUR:4.99" }, @@ -2354,41 +2394,15 @@ run (void *cls) .details.track_transfer.expected_pay_ref = "deposit-simple" }, - /* Trace transaction to WTID */ - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-1", - .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.pay_ref = "deposit-simple", - .details.track_transaction.expected_transfer_ref = "check_bank_transfer-499c" - }, - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-1-again", - .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.pay_ref = "deposit-simple", - .details.track_transaction.expected_transfer_ref = "check_bank_transfer-499c" - }, - /* Pay again successfully on 2nd contract */ { .oc = OC_PAY, .label = "deposit-simple-2", .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-contract-2", + .details.pay.contract_ref = "create-proposal-2", .details.pay.coin_ref = "withdraw-coin-2", .details.pay.amount_with_fee = "EUR:5", .details.pay.amount_without_fee = "EUR:4.99" }, - /* Check "failure" to trace transaction to WTID before aggregator */ - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-2-found", - .expected_response_code = MHD_HTTP_ACCEPTED, - .details.track_transaction.pay_ref = "deposit-simple-2" - }, - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-2-found-again", - .expected_response_code = MHD_HTTP_ACCEPTED, - .details.track_transaction.pay_ref = "deposit-simple-2" - }, - /* Run transfers. */ { .oc = OC_RUN_AGGREGATOR, .label = "run-aggregator-2" }, @@ -2405,20 +2419,6 @@ run (void *cls) { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, .label = "check_bank_empty" }, - /* This time, invert the order in which we do the tracing */ - /* Trace transaction to WTID */ - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-2", - .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.pay_ref = "deposit-simple-2", - .details.track_transaction.expected_transfer_ref = "check_bank_transfer-499c-2" - }, - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-2-again", .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.pay_ref = "deposit-simple-2", - .details.track_transaction.expected_transfer_ref = "check_bank_transfer-499c-2" - }, - /* Trace the WTID back to the original transaction */ { .oc = OC_TRACK_TRANSFER, .label = "track-transfer-2", @@ -2448,27 +2448,6 @@ run (void *cls) .details.history.date.abs_value_us = 43 * 1000LL * 1000LL, .details.history.nresult = 0 }, - /* Retrieve via /map/out a contract NOT stored previously. */ - { - .oc = OC_PROPOSAL_LOOKUP, - .label = "fetch-proposal-not-found", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.proposal_lookup.proposal_reference = "create-proposal-3" }, - - /* Create another contract, NOT to be stored. */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-3", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ - {\"currency\":\"EUR\", \"value\":0, \"fraction\":10000},\ - \"transaction_id\":3,\ - \"timestamp\":\"\\/Date(42)\\/\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(0)\\/\",\ - \"amount\":{\"currency\":\"EUR\", \"value\":1, \"fraction\":0},\ - \"products\":\ - [ {\"description\":\"bogus\", \"value\":\"{EUR:1}\"} ] }" }, /* end of testcase */ { .oc = OC_END } @@ -2628,6 +2607,7 @@ main (int argc, "taler-merchant-httpd", "taler-merchant-httpd", "-c", "test_merchant_api.conf", + "-L", "DEBUG", NULL); if (NULL == merchantd) {