commit 552e5882589fe22838bd2519a2491d4b4d2cdce3
parent 29976e055994339b651c9a26e3a1adb873625f22
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 31 May 2016 17:31:59 +0200
Merge branch 'master' of git+ssh://taler.net/var/git/merchant
Diffstat:
22 files changed, 825 insertions(+), 331 deletions(-)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
@@ -21,7 +21,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
taler-merchant-httpd_contract.c taler-merchant-httpd_contract.h \
taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
- taler-merchant-httpd_util.c taler-merchant-httpd_util.h
+ taler-merchant-httpd_track.c taler-merchant-httpd_track.h
+
taler_merchant_httpd_LDADD = \
$(top_srcdir)/src/backenddb/libtalermerchantdb.la \
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
@@ -20,10 +20,11 @@ KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
DB = postgres
# Which wireformat does this merchant use? (test/sepa/etc.)
-# WIREFORMAT = "test"
+WIREFORMAT = test
# Determines which wire plugin will be used. We currently only
# support one wire plugin at a time!
+WIRE_TRANSFER_DELAY = 3 week
# Configuration for postgres database.
[merchantdb-postgres]
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -37,7 +37,7 @@
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd_contract.h"
#include "taler-merchant-httpd_pay.h"
-#include "taler-merchant-httpd_util.h"
+#include "taler-merchant-httpd_track.h"
/**
@@ -80,7 +80,7 @@ static char *keyfile;
* This value tells the exchange by which date this merchant would like
* to receive the funds for a deposited payment
*/
-struct GNUNET_TIME_Relative edate_delay;
+struct GNUNET_TIME_Relative wire_transfer_delay;
/**
* Which currency is supported by this merchant?
@@ -174,12 +174,6 @@ 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 },
- { "/hash-contract", MHD_HTTP_METHOD_POST, "application/json",
- NULL, 0,
- &MH_handler_hash_contract, MHD_HTTP_OK },
- { "/hash-contract", NULL, "text/plain",
- "Only POST is allowed", 0,
- &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
{ "/contract", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_contract, MHD_HTTP_OK },
@@ -193,6 +187,12 @@ url_handler (void *cls,
{ "/pay", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/track/deposit", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &MH_handler_track_deposit, MHD_HTTP_OK},
+ { "/track/deposit", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK},
{NULL, NULL, NULL, NULL, 0, 0 }
};
@@ -483,6 +483,10 @@ run (void *cls,
result = GNUNET_SYSERR;
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-merchant-httpd",
+ "INFO",
+ NULL));
if (GNUNET_SYSERR ==
TMH_EXCHANGES_init (config))
{
@@ -511,12 +515,12 @@ run (void *cls,
if (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_time (config,
"merchant",
- "EDATE",
- &edate_delay))
+ "WIRE_TRANSFER_DELAY",
+ &wire_transfer_delay))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"merchant",
- "EDATE");
+ "WIRE_TRANSFER_DELAY");
GNUNET_SCHEDULER_shutdown ();
return;
}
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
@@ -179,10 +179,10 @@ extern struct TALER_MERCHANTDB_Plugin *db;
/**
* If the frontend does NOT specify an execution date, how long should
* we tell the exchange to wait to aggregate transactions before
- * executing? This delay is added to the current time when we
- * generate the advisory execution time for the exchange.
+ * executing the wire transfer? This delay is added to the current
+ * time when we generate the advisory execution time for the exchange.
*/
-extern struct GNUNET_TIME_Relative edate_delay;
+extern struct GNUNET_TIME_Relative wire_transfer_delay;
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
@@ -57,7 +57,7 @@ check_products (json_t *products)
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("description", &description),
/* FIXME: there are other fields in the product specification
- that rre currently not labeled as optional. Maybe check
+ that are currently not labeled as optional. Maybe check
those as well, or make them truly optional. */
GNUNET_JSON_spec_end()
};
@@ -187,29 +187,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
"products in contract request malformed");
}
- /* Check if this transaction ID erroneously corresponds to a
- contract that already paid, in which case we should refuse
- to sign it again (frontend buggy, it should use a fresh
- transaction ID each time)! */
- if (GNUNET_OK ==
- db->check_payment (db->cls,
- transaction_id))
- {
- struct MHD_Response *resp;
- int ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Transaction %llu already paid in the past, refusing to sign!\n",
- (unsigned long long) transaction_id);
- resp = MHD_create_response_from_buffer (strlen ("Duplicate transaction ID!"),
- "Duplicate transaction ID!",
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_FORBIDDEN,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
/* add fields to the contract that the backend should provide */
json_object_set (jcontract,
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
@@ -24,10 +24,26 @@
#include "taler-merchant-httpd_exchanges.h"
+
+/**
+ * Delay after which we'll re-fetch key information from the exchange.
+ */
+#define RELOAD_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
+
/**
- * How often do we retry fetching /keys?
+ * Threshold after which exponential backoff should not increase.
*/
-#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
+#define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
+
+
+/**
+ * Perform our exponential back-off calculation, starting at 1 ms
+ * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD.
+ *
+ * @param r current backoff time, initially zero
+ */
+#define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, (r)), 2));
/**
@@ -68,7 +84,8 @@ struct TMH_EXCHANGES_FindOperation
struct Exchange *my_exchange;
/**
- * Task scheduled to asynchrnously return the result.
+ * Task scheduled to asynchronously return the result to
+ * the find continuation.
*/
struct GNUNET_SCHEDULER_Task *at;
@@ -118,9 +135,9 @@ struct Exchange
struct TALER_MasterPublicKeyP master_pub;
/**
- * At what time should we try to fetch /keys again?
+ * How long should we wait between the next retry?
*/
- struct GNUNET_TIME_Absolute retry_time;
+ struct GNUNET_TIME_Relative retry_delay;
/**
* Task where we retry fetching /keys from the exchange.
@@ -128,8 +145,9 @@ struct Exchange
struct GNUNET_SCHEDULER_Task *retry_task;
/**
- * Flag which indicates whether some HTTP transfer between
- * this merchant and the exchange is still ongoing
+ * #GNUNET_YES to indicate that there is an ongoing
+ * transfer we are waiting for,
+ * #GNUNET_NO to indicate that key data is up-to-date.
*/
int pending;
@@ -201,12 +219,16 @@ retry_exchange (void *cls)
{
struct Exchange *exchange = cls;
+ /* might be a scheduled reload and not our first attempt */
exchange->retry_task = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Connecting to exchange exchange %s in retry_exchange\n",
exchange->uri);
-
- exchange->pending = GNUNET_SYSERR; /* failed hard */
+ if (NULL != exchange->conn)
+ {
+ TALER_EXCHANGE_disconnect (exchange->conn);
+ exchange->conn = NULL;
+ }
exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
exchange->uri,
&keys_mgmt_cb,
@@ -236,39 +258,41 @@ keys_mgmt_cb (void *cls,
{
struct Exchange *exchange = cls;
struct TMH_EXCHANGES_FindOperation *fo;
+ struct GNUNET_TIME_Absolute expire;
+ struct GNUNET_TIME_Relative delay;
- if (NULL != keys)
- {
- exchange->pending = GNUNET_NO;
- }
- else
+ if (NULL == keys)
{
+ exchange->pending = GNUNET_YES;
+ exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to fetch /keys from `%s'\n",
- exchange->uri);
- TALER_EXCHANGE_disconnect (exchange->conn);
- exchange->conn = NULL;
- exchange->retry_time = GNUNET_TIME_relative_to_absolute (KEYS_RETRY_FREQ);
- /* Always retry trusted exchanges in the background, so that we don't have
- * to wait for a customer to trigger it and thus delay his response */
- if (GNUNET_YES == exchange->trusted)
- {
- exchange->retry_task = GNUNET_SCHEDULER_add_delayed (KEYS_RETRY_FREQ,
- &retry_exchange,
- exchange);
- }
- else
- {
- exchange->pending = GNUNET_SYSERR; /* failed hard */
- }
+ "Failed to fetch /keys from `%s', retrying in %s\n",
+ exchange->uri,
+ GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
+ GNUNET_YES));
+ exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
+ &retry_exchange,
+ exchange);
+ return;
}
+ expire = TALER_EXCHANGE_check_keys_current (exchange->conn);
+ if (0 == expire.abs_value_us)
+ delay = RELOAD_DELAY;
+ else
+ delay = GNUNET_TIME_absolute_get_remaining (expire);
+ exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
+ exchange->retry_task
+ = GNUNET_SCHEDULER_add_delayed (delay,
+ &retry_exchange,
+ exchange);
+ exchange->pending = GNUNET_NO;
while (NULL != (fo = exchange->fo_head))
{
GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
exchange->fo_tail,
fo);
fo->fc (fo->fc_cls,
- (NULL != keys) ? exchange->conn : NULL,
+ exchange->conn,
exchange->trusted);
GNUNET_free (fo);
}
@@ -312,7 +336,7 @@ return_result (void *cls)
*/
struct TMH_EXCHANGES_FindOperation *
TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
- TMH_EXCHANGES_FindContinuation fc, // process payment
+ TMH_EXCHANGES_FindContinuation fc,
void *fc_cls)
{
struct Exchange *exchange;
@@ -359,29 +383,6 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
chosen_exchange);
}
- if (GNUNET_SYSERR == exchange->pending)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Maybe retrying previously contacted exchange `%s'\n",
- chosen_exchange);
- /* check if we should resume this exchange */
- if (0 == GNUNET_TIME_absolute_get_remaining (exchange->retry_time).rel_value_us)
- {
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Retrying exchange `%s'\n",
- chosen_exchange);
- exchange->pending = GNUNET_YES;
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Not retrying exchange `%s', too early\n",
- chosen_exchange);
- }
- }
-
-
fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
fo->fc = fc;
fo->fc_cls = fc_cls;
@@ -390,7 +391,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
exchange->fo_tail,
fo);
- if (GNUNET_YES != exchange->pending) // can post coins
+ if (GNUNET_YES != exchange->pending)
{
/* We are not currently waiting for a reply, immediately
return result */
@@ -434,13 +435,14 @@ TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
/**
* Function called on each configuration section. Finds sections
- * about exchanges and parses the entries.
+ * about exchanges, parses the entries and tries to connect to
+ * it in order to fetch /keys.
*
* @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
* @param section name of the section
*/
static void
-parse_exchanges (void *cls,
+accept_exchanges (void *cls,
const char *section)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
@@ -524,10 +526,11 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
return GNUNET_SYSERR;
}
merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
+ /* get exchanges from the merchant configuration and try to connect to them */
GNUNET_CONFIGURATION_iterate_sections (cfg,
- &parse_exchanges,
+ &accept_exchanges,
(void *) cfg);
- /* build JSON with list of trusted exchanges */
+ /* build JSON with list of trusted exchanges (will be included in contracts) */
trusted_exchanges = json_array ();
for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
{
diff --git a/src/backend/taler-merchant-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c
@@ -41,10 +41,10 @@
*/
int
TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
{
struct MHD_Response *response;
int ret;
diff --git a/src/backend/taler-merchant-httpd_parsing.c b/src/backend/taler-merchant-httpd_parsing.c
@@ -27,12 +27,6 @@
#include "taler-merchant-httpd_parsing.h"
#include "taler-merchant-httpd_responses.h"
-/* Although the following declaration isn't in any case useful
-to a merchant's activity, it's needed here to make the function
-'TMH_PARSE_nagivate_json ()' compile fine; so its value will be
-kept on some merchant's accepted currency. For multi currencies
-merchants, that of course would require a patch */
-extern char *TMH_merchant_currency_string;
/**
* Initial size for POST request buffer.
@@ -129,9 +123,9 @@ buffer_append (struct Buffer *buf,
if (data_size + buf->fill > buf->alloc)
{
char *new_buf;
- size_t new_size = buf->alloc;
+ size_t new_size = buf->alloc ? buf->alloc : 1;
while (new_size < buf->fill + data_size)
- new_size += 2;
+ new_size *= 2;
if (new_size > max_size)
return GNUNET_NO;
new_buf = GNUNET_malloc (new_size);
@@ -313,4 +307,49 @@ TMH_PARSE_json_data (struct MHD_Connection *connection,
}
+
+/**
+ * Extract base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is
+ * missing or invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size)
+{
+ const char *str;
+
+ str = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ param_name);
+ if (NULL == str)
+ {
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_missing (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ out_data,
+ out_size))
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_invalid (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
/* end of taler-merchant-httpd_parsing.c */
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
@@ -18,6 +18,7 @@
* @brief handling of /pay requests
* @author Marcello Stanisci
* @author Christian Grothoff
+ * @author Florian Dold
*/
#include "platform.h"
#include <jansson.h>
@@ -33,6 +34,12 @@
/**
+ * How long to wait before giving up processing with the exchange?
+ */
+#define PAY_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30))
+
+
+/**
* Information we keep for an individual call to the /pay handler.
*/
struct PayContext;
@@ -175,11 +182,11 @@ struct PayContext
struct GNUNET_HashCode h_contract;
/**
- * Execution date. How soon would the merchant like the
- * transaction to be executed? (Can be given by the frontend
- * or be determined by our configuration via #edate_delay.)
+ * Wire transfer deadline. How soon would the merchant like the
+ * wire transfer to be executed? (Can be given by the frontend
+ * or be determined by our configuration via #wire_transfer_delay.)
*/
- struct GNUNET_TIME_Absolute edate;
+ struct GNUNET_TIME_Absolute wire_transfer_deadline;
/**
* Response to return, NULL if we don't have one yet.
@@ -206,6 +213,13 @@ struct PayContext
*/
unsigned int response_code;
+ /**
+ * Task called when the (suspended) processing for
+ * the /pay request times out.
+ * Happens when we don't get a response from the exchange.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+
};
@@ -228,10 +242,16 @@ resume_pay_with_response (struct PayContext *pc,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming /pay handling as exchange interaction is done (%u)\n",
response_code);
+ if (NULL != pc->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (pc->timeout_task);
+ pc->timeout_task = NULL;
+ }
MHD_resume_connection (pc->connection);
TMH_trigger_daemon (); /* we resumed, kick MHD */
}
+
/**
* Convert denomination key to its base32 representation
*
@@ -311,10 +331,13 @@ deposit_cb (void *cls,
if (NULL == proof)
{
- /* FIXME: is this the right code for when the exchange fails? */
+ /* We can't do anything meaningful here, the exchange did something wrong */
+ /* FIXME: any useful information we can include? */
resume_pay_with_response (pc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TMH_RESPONSE_make_internal_error ("Exchange failed, no proof available"));
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:s}",
+ "error", "exchange failed",
+ "hint", "The exchange provided an unexpected response"));
}
else
{
@@ -375,6 +398,12 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
struct PayContext *pc = (struct PayContext *) hc;
unsigned int i;
+ if (NULL != pc->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (pc->timeout_task);
+ pc->timeout_task = NULL;
+ }
+
TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
for (i=0;i<pc->coins_cnt;i++)
{
@@ -466,9 +495,10 @@ process_pay_with_exchange (void *cls,
GNUNET_break_op (0);
resume_pay_with_response (pc,
MHD_HTTP_BAD_REQUEST,
- TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
- "hint", "unknown denom to exchange",
- "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key)));
+ TMH_RESPONSE_make_json_pack ("{s:s, s:o, s:o}",
+ "error", "denomination not found",
+ "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key),
+ "exchange_keys", TALER_EXCHANGE_get_keys_raw (mh)));
denom_enc = denomination_to_string_alloc (&dc->denom);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unknown denom to exchange: %s\n", denom_enc);
GNUNET_free (denom_enc);
@@ -484,10 +514,10 @@ process_pay_with_exchange (void *cls,
resume_pay_with_response (pc,
MHD_HTTP_BAD_REQUEST,
TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
- "hint", "no acceptable auditor for denomination",
+ "error", "invalid denomination",
"denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key)));
denom_enc = denomination_to_string_alloc (&dc->denom);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no acceptable auditor for denomination: %s\n", denom_enc);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "client offered invalid denomination: %s\n", denom_enc);
GNUNET_free (denom_enc);
return;
}
@@ -593,7 +623,7 @@ process_pay_with_exchange (void *cls,
dc->dh = TALER_EXCHANGE_deposit (mh,
&dc->percoin_amount,
- pc->edate,
+ pc->wire_transfer_deadline,
j_wire,
&pc->h_contract,
&dc->coin_pub,
@@ -623,6 +653,33 @@ process_pay_with_exchange (void *cls,
/**
+ * Handle a timeout for the processing of the pay request.
+ *
+ * @param cls closure
+ */
+static void
+handle_pay_timeout (void *cls)
+{
+ struct PayContext *pc = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Resuming /pay with error after timeout\n");
+
+ pc->timeout_task = NULL;
+
+ if (NULL != pc->fo)
+ {
+ TMH_EXCHANGES_find_exchange_cancel (pc->fo);
+ pc->fo = NULL;
+ }
+
+ resume_pay_with_response (pc,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TMH_RESPONSE_make_internal_error ("exchange not reachable"));
+}
+
+
+/**
* Accomplish this payment.
*
* @param rh context of the handler
@@ -748,18 +805,24 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
"invalid merchant signature supplied");
}
- /* 'edate' is optional, if it is not present, generate it here; it
- will be timestamp plus the edate_delay supplied in config
- file */
- if (NULL == json_object_get (root, "edate"))
+ /* 'wire_transfer_deadline' is optional, if it is not present,
+ generate it here; it will be timestamp plus the
+ wire_transfer_delay supplied in config file */
+ if (NULL == json_object_get (root, "wire_transfer_deadline"))
{
- pc->edate = GNUNET_TIME_absolute_add (pc->timestamp,
- edate_delay);
+ pc->wire_transfer_deadline = GNUNET_TIME_absolute_add (pc->timestamp,
+ wire_transfer_delay);
+ if (pc->wire_transfer_deadline.abs_value_us < pc->refund_deadline.abs_value_us)
+ {
+ /* Refund value very large, delay wire transfer accordingly */
+ pc->wire_transfer_deadline = pc->refund_deadline;
+ }
}
else
{
struct GNUNET_JSON_Specification espec[] = {
- GNUNET_JSON_spec_absolute_time ("edate", &pc->edate),
+ GNUNET_JSON_spec_absolute_time ("wire_transfer_deadline",
+ &pc->wire_transfer_deadline),
GNUNET_JSON_spec_end()
};
@@ -772,6 +835,14 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
GNUNET_break (0);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
+ if (pc->wire_transfer_deadline.abs_value_us < pc->refund_deadline.abs_value_us)
+ {
+ GNUNET_break (0);
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "refund deadline after wire transfer deadline");
+ }
+
}
@@ -833,9 +904,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
/* Payment succeeded in the past; take short cut
and accept immediately */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Transaction %llu already paid in the past, taking short cut.\n",
- (unsigned long long) pc->transaction_id);
resp = MHD_create_response_from_buffer (0,
NULL,
MHD_RESPMEM_PERSISTENT);
@@ -859,6 +927,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Suspending /pay handling while working with the exchange\n");
MHD_suspend_connection (connection);
+ pc->timeout_task = GNUNET_SCHEDULER_add_delayed (PAY_TIMEOUT, handle_pay_timeout, pc);
json_decref (root);
return MHD_YES;
}
diff --git a/src/backend/taler-merchant-httpd_responses.c b/src/backend/taler-merchant-httpd_responses.c
@@ -289,5 +289,41 @@ TMH_RESPONSE_make_external_error (const char *hint)
"hint", hint);
}
+/**
+ * Send a response indicating a missing argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{ s:s, s:s}",
+ "error", "missing parameter",
+ "parameter", param_name);
+}
+
+
+/**
+ * Send a response indicating an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s}",
+ "error", "invalid parameter",
+ "parameter", param_name);
+}
/* end of taler-exchange-httpd_responses.c */
diff --git a/src/backend/taler-merchant-httpd_responses.h b/src/backend/taler-merchant-httpd_responses.h
@@ -157,4 +157,25 @@ TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection);
void
TMH_RESPONSE_add_global_headers (struct MHD_Response *response);
+/**
+ * Send a response indicating a missing argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
+ const char *param_name);
+
+/**
+ * Send a response indicating an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name);
#endif
diff --git a/src/backend/taler-merchant-httpd_track.c b/src/backend/taler-merchant-httpd_track.c
@@ -0,0 +1,70 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_track.c
+ * @brief implement API for tracking deposits and wire transfers
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_json_lib.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.h"
+
+
+extern char *TMH_merchant_currency_string;
+
+
+/**
+ * Manages a /track/deposit call, thus it calls the /wire/deposit
+ * offered by the exchange in order to return the set of deposits
+ * (of coins) associated with a given wire transfer
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+MH_handler_track_deposit (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ static struct TMH_RequestHandler pong =
+ {
+ "", NULL, "text/html",
+ "/track/deposit served\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "serving /track/deposit\n");
+ return TMH_MHD_handler_static_response (&pong,
+ connection,
+ connection_cls,
+ upload_data,
+ upload_data_size);
+}
+
+/* end of taler-merchant-httpd_contract.c */
diff --git a/src/backend/taler-merchant-httpd_track.h b/src/backend/taler-merchant-httpd_track.h
@@ -0,0 +1,41 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_track.h
+ * @brief headers for /track/{deposit,wtid} handler
+ * @author Marcello Stanisci
+ */
+#include <microhttpd.h>
+#include "taler-merchant-httpd.h"
+
+/**
+ * Manages a /track/deposit call, thus it calls the /wire/deposit
+ * offered by the exchange in order to return the set of deposits
+ * (of coins) associated with a given wire transfer
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+MH_handler_track_deposit (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
diff --git a/src/backend/taler-merchant-httpd_util.c b/src/backend/taler-merchant-httpd_util.c
@@ -1,105 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file backend/taler-merchant-httpd_contract.c
- * @brief HTTP serving layer mainly intended to communicate with the frontend
- * @author Marcello Stanisci
- */
-#include "platform.h"
-#include <jansson.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_json_lib.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_parsing.h"
-#include "taler-merchant-httpd_auditors.h"
-#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_responses.h"
-
-
-/**
- * Hashes a plain JSON contract sending the result to the other end of
- * HTTP communication
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-MH_handler_hash_contract (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- json_t *root;
- json_t *jcontract;
- int res;
- struct GNUNET_HashCode hc;
- struct TMH_JsonParseContext *ctx;
-
- if (NULL == *connection_cls)
- {
- ctx = GNUNET_new (struct TMH_JsonParseContext);
- ctx->hc.cc = &TMH_json_parse_cleanup;
- *connection_cls = ctx;
- }
- else
- {
- ctx = *connection_cls;
- }
-
- res = TMH_PARSE_post_json (connection,
- &ctx->json_parse_context,
- upload_data,
- upload_data_size,
- &root);
-
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- /* the POST's body has to be further fetched */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- jcontract = json_object_get (root, "contract");
-
- if (NULL == jcontract)
- {
- return TMH_RESPONSE_reply_external_error (connection,
- "missing 'contract' field");
- }
-
- if (GNUNET_OK != TALER_JSON_hash (jcontract,
- &hc))
- {
- return TMH_RESPONSE_reply_external_error (connection,
- "expected object as contract");
- }
-
- GNUNET_assert (GNUNET_OK ==
- TALER_JSON_hash (jcontract,
- &hc));
-
- /* return final response */
- res = TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:O}",
- "hash", GNUNET_JSON_from_data_auto (&hc));
- json_decref (root);
- return res;
-}
diff --git a/src/backend/taler-merchant-httpd_util.h b/src/backend/taler-merchant-httpd_util.h
@@ -1,43 +0,0 @@
-/*
- This file is part of TALER
- (C) 2014, 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file backend/taler-merchant-httpd_contract.h
- * @brief headers for /contract handler
- * @author Marcello Stanisci
- */
-#ifndef TALER_EXCHANGE_HTTPD_UTIL_H
-#define TALER_EXCHANGE_HTTPD_UTIL_H
-#include <microhttpd.h>
-#include "taler-merchant-httpd.h"
-
-/**
- * Manage a contract request
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-MH_handler_hash_contract (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-#endif
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
@@ -17,6 +17,7 @@
* @file include/taler_merchant_service.h
* @brief C interface of libtalermerchant, a C library to use merchant's HTTP API
* @author Christian Grothoff
+ * @author Marcello Stanisci
*/
#ifndef _TALER_MERCHANT_SERVICE_H
#define _TALER_MERCHANT_SERVICE_H
@@ -25,6 +26,49 @@
#include <gnunet/gnunet_curl_lib.h>
#include <jansson.h>
+/* ********************* /track/deposit *********************** */
+
+/**
+ * @brief Handle to a /contract operation at a merchant's backend.
+ */
+struct TALER_MERCHANT_TrackDepositOperation;
+
+/**
+ * Callbacks of this type are used to work the result of submitting a /track/deposit request to a merchant
+ */
+typedef void
+(*TALER_MERCHANT_TrackDepositCallback) (void *cls,
+ unsigned int http_status,
+ const json_t *obj);
+
+/**
+ * Request backend to return deposits associated with a given wtid.
+ *
+ * @param ctx execution context
+ * @param backend_uri URI of the backend (having /track/deposit appended)
+ * @param wtid base32 string indicating a wtid
+ * @param exchange base URL of the exchange in charge of returning the wanted information
+ * @param trackdeposit_cb the callback to call when a reply for this request is available
+ * @param trackdeposit_cb_cls closure for @a contract_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_TrackDepositOperation *
+TALER_MERCHANT_track_deposit (struct GNUNET_CURL_Context *ctx,
+ const char *backend_uri,
+ const char *wtid,
+ const char *exchange_uri,
+ TALER_MERCHANT_TrackDepositCallback trackdeposit_cb,
+ void *trackdeposit_cb_cls);
+
+/**
+ * Cancel a /track/deposit request. This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param co the deposit's tracking operation
+ */
+void
+TALER_MERCHANT_track_deposit_cancel (struct TALER_MERCHANT_TrackDepositOperation *tdo);
+
/* ********************* /contract *********************** */
@@ -196,8 +240,8 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
/**
- * Information we need from the frontend when forwarding
- * a payment to the backend.
+ * Information we need from the frontend (ok, the frontend sends just JSON)
+ * when forwarding a payment to the backend.
*/
struct TALER_MERCHANT_PaidCoin
{
@@ -254,7 +298,7 @@ struct TALER_MERCHANT_PaidCoin
* @param merchant_sig the signature of the merchant over the original contract
* @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
* @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
- * @param execution_deadline date by which the merchant would like the exchange to execute the transaction (can be zero if there is no specific date desired by the frontend). If non-zero, must be larger than @a refund_deadline.
+ * @param wire_transfer_deadline date by which the merchant would like the exchange to execute the wire transfer (can be zero if there is no specific date desired by the frontend). If non-zero, must be larger than @a refund_deadline.
* @param exchange_uri URI of the exchange that the coins belong to
* @param num_coins number of coins used to pay
* @param coins array of coins we use to pay
@@ -273,7 +317,7 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
const struct TALER_MerchantSignatureP *merchant_sig,
struct GNUNET_TIME_Absolute refund_deadline,
struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute execution_deadline,
+ struct GNUNET_TIME_Absolute wire_transfer_deadline,
const char *exchange_uri,
unsigned int num_coins,
const struct TALER_MERCHANT_PaidCoin *coins,
@@ -295,4 +339,24 @@ void
TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *ph);
+
+/**
+ * Request backend to return deposits associated with a given wtid.
+ *
+ * @param ctx execution context
+ * @param backend_uri URI of the backend
+ * @param wtid base32 string indicating a wtid
+ * @param exchange_uri base URL of the exchange in charge of returning the wanted information
+ * @param trackdeposit_cb the callback to call when a reply for this request is available
+ * @param trackdeposit_cb_cls closure for @a contract_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_TrackDepositOperation *
+TALER_MERCHANT_track_deposit (struct GNUNET_CURL_Context *ctx,
+ const char *backend_uri,
+ const char *wtid,
+ const char *exchange_uri,
+ TALER_MERCHANT_TrackDepositCallback trackdeposit_cb,
+ void *trackdeposit_cb_cls);
+
#endif /* _TALER_MERCHANT_SERVICE_H */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -15,7 +15,8 @@ libtalermerchant_la_LDFLAGS = \
libtalermerchant_la_SOURCES = \
merchant_api_contract.c \
- merchant_api_pay.c
+ merchant_api_pay.c \
+ merchant_api_track.c
libtalermerchant_la_LIBADD = \
-ltalerexchange \
diff --git a/src/lib/merchant_api_contract.c b/src/lib/merchant_api_contract.c
@@ -123,7 +123,6 @@ handle_contract_finished (void *cls,
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_FORBIDDEN:
- /* Duplicate transaction ID, frontend is buggy! */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, merchant says one of the signatures is
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
@@ -358,7 +358,7 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
* @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
* @param transaction_id transaction id for the transaction between merchant and customer
* @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
- * @param execution_deadline date by which the merchant would like the exchange to execute the transaction (can be zero if there is no specific date desired by the frontend). If non-zero, must be larger than @a refund_deadline.
+ * @param wire_transfer_deadline date by which the merchant would like the exchange to execute the wire transfer (can be zero if there is no specific date desired by the frontend). If non-zero, must be larger than @a refund_deadline.
* @param num_coins number of coins used to pay
* @param coins array of coins we use to pay
* @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
@@ -378,7 +378,7 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
const struct TALER_MerchantSignatureP *merchant_sig,
struct GNUNET_TIME_Absolute refund_deadline,
struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute execution_deadline,
+ struct GNUNET_TIME_Absolute wire_transfer_deadline,
const char *exchange_uri,
unsigned int num_coins,
const struct TALER_MERCHANT_PaidCoin *coins,
@@ -400,8 +400,8 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0);
return NULL;
}
- if ( (0 != execution_deadline.abs_value_us) &&
- (execution_deadline.abs_value_us < refund_deadline.abs_value_us) )
+ if ( (0 != wire_transfer_deadline.abs_value_us) &&
+ (wire_transfer_deadline.abs_value_us < wire_transfer_deadline.abs_value_us) )
{
GNUNET_break (0);
return NULL;
@@ -549,12 +549,12 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
"amount", TALER_JSON_from_amount (amount),
"merchant_sig", GNUNET_JSON_from_data_auto (merchant_sig));
- if (0 != execution_deadline.abs_value_us)
+ if (0 != wire_transfer_deadline.abs_value_us)
{
/* Frontend did have an execution date in mind, add it */
json_object_set_new (pay_obj,
- "edate",
- GNUNET_JSON_from_time_abs (execution_deadline));
+ "wire_transfer_deadline",
+ GNUNET_JSON_from_time_abs (wire_transfer_deadline));
}
ph = GNUNET_new (struct TALER_MERCHANT_Pay);
diff --git a/src/lib/merchant_api_track.c b/src/lib/merchant_api_track.c
@@ -0,0 +1,188 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING.LGPL. If not, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_contract.c
+ * @brief Implementation of the /track/deposit and /track/wtid request of the
+ * merchant's HTTP API
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_merchant_service.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A Contract Operation Handle
+ */
+struct TALER_MERCHANT_TrackDepositOperation
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * base32 identifier being the 'witd' parameter required by the
+ * exchange
+ */
+ char *wtid;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TrackDepositCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /track/deposit request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TrackDepositOperation`
+ * @param response_code HTTP response code, 0 on error
+ * @param json response body, NULL if not in JSON
+ */
+static void
+handle_trackdeposit_finished (void *cls,
+ long response_code,
+ const json_t *json) // body
+{
+ struct TALER_MERCHANT_TrackDepositOperation *tdo = cls;
+
+ tdo->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ {
+ /* Work out argument for external callback from the body .. */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "200 returned from /track/deposit");
+ }
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "track deposit URI not found");
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ /* FIXME figure out wich parameters ought to be passed back */
+ tdo->cb (tdo->cb_cls,
+ response_code,
+ json);
+}
+
+
+/**
+ * Request backend to return deposits associated with a given wtid.
+ *
+ * @param ctx execution context
+ * @param backend_uri URI of the backend (having /track/deposit appended)
+ * @param wtid base32 string indicating a wtid
+ * @param exchange base URL of the exchange in charge of returning the wanted information
+ * @param trackdeposit_cb the callback to call when a reply for this request is available
+ * @param trackdeposit_cb_cls closure for @a contract_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_TrackDepositOperation *
+TALER_MERCHANT_track_deposit (struct GNUNET_CURL_Context *ctx,
+ const char *backend_uri,
+ const char *wtid,
+ const char *exchange_uri,
+ TALER_MERCHANT_TrackDepositCallback trackdeposit_cb,
+ void *trackdeposit_cb_cls)
+{
+ struct TALER_MERCHANT_TrackDepositOperation *tdo;
+ CURL *eh;
+
+ tdo = GNUNET_new (struct TALER_MERCHANT_TrackDepositOperation);
+ tdo->ctx = ctx;
+ tdo->cb = trackdeposit_cb;
+ tdo->cb_cls = trackdeposit_cb_cls;
+ /* TO BE FREED SOMEWHERE. GOTTEN WITH /track/deposit ALREADY APPENDED */
+ GNUNET_asprintf (&tdo->url,
+ "%s?wtid=%s&exchange=%s",
+ backend_uri,
+ wtid,
+ exchange_uri);
+ eh = curl_easy_init ();
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ tdo->url));
+ tdo->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_trackdeposit_finished,
+ tdo);
+ return tdo;
+}
+
+/**
+ * Cancel a /track/deposit request. This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param co the deposit's tracking operation
+ */
+void
+TALER_MERCHANT_track_deposit_cancel (struct TALER_MERCHANT_TrackDepositOperation *tdo)
+{
+ if (NULL != tdo->job)
+ {
+ GNUNET_CURL_job_cancel (tdo->job);
+ tdo->job = NULL;
+ }
+ GNUNET_free (tdo->url);
+ GNUNET_free (tdo->wtid);
+ GNUNET_free (tdo);
+}
+
+/* end of merchant_api_track.c */
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
@@ -104,7 +104,12 @@ enum OpCode
/**
* Pay with coins.
*/
- OC_PAY
+ OC_PAY,
+
+ /**
+ * Retrieve deposit permissions for a given wire transfer
+ */
+ OC_TRACK_DEPOSIT
};
@@ -203,9 +208,14 @@ struct Command
const char *amount;
/**
- * Wire details (JSON).
+ * Sender's bank account details (JSON).
*/
- const char *wire;
+ const char *sender_details;
+
+ /**
+ * Transfer details (JSON)
+ */
+ const char *transfer_details;
/**
* Set (by the interpreter) to the reserve's private key
@@ -283,7 +293,7 @@ struct Command
/**
* Blinding key used for the operation.
*/
- struct TALER_DenominationBlindingKey blinding_key;
+ struct TALER_DenominationBlindingKeyP blinding_key;
/**
* Withdraw handle (while operation is running).
@@ -370,6 +380,20 @@ struct Command
} pay;
+ struct {
+
+ /**
+ * Wire transfer ID whose deposit permissions are to be retrieved
+ */
+ char *wtid;
+
+ /**
+ * Handle to a /track/deposit operation
+ */
+ struct TALER_MERCHANT_TrackDepositOperation *tdo;
+
+ } track_deposit;
+
} details;
};
@@ -813,6 +837,30 @@ pay_cb (void *cls,
is);
}
+/**
+ * Callback for a /track/deposit operation
+ *
+ * @param cls closure for this function
+ * @param http_status HTTP response code returned by the server
+ * @param obj server response's body
+ */
+static void
+track_deposit_cb (void *cls,
+ unsigned int http_status,
+ const json_t *obj)
+{
+ if (MHD_HTTP_OK == http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ok from /track/deposit handler\n");
+ result = GNUNET_OK;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not Ok from /track/deposit handler\n");
+ result = GNUNET_SYSERR;
+ }
+}
+
/**
* Find denomination key matching the given amount.
@@ -884,7 +932,8 @@ interpreter_run (void *cls)
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_Amount amount;
struct GNUNET_TIME_Absolute execution_date;
- json_t *wire;
+ json_t *sender_details;
+ json_t *transfer_details;
is->task = NULL;
tc = GNUNET_SCHEDULER_get_task_context ();
@@ -938,28 +987,46 @@ interpreter_run (void *cls)
fail (is);
return;
}
- wire = json_loads (cmd->details.admin_add_incoming.wire,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == wire)
+
+ execution_date = GNUNET_TIME_absolute_get ();
+ GNUNET_TIME_round_abs (&execution_date);
+ sender_details = json_loads (cmd->details.admin_add_incoming.sender_details,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == sender_details)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse wire details `%s' at %u\n",
- cmd->details.admin_add_incoming.wire,
+ "Failed to parse sender details `%s' at %u\n",
+ cmd->details.admin_add_incoming.sender_details,
is->ip);
fail (is);
return;
}
- execution_date = GNUNET_TIME_absolute_get ();
- GNUNET_TIME_round_abs (&execution_date);
+ transfer_details = json_loads (cmd->details.admin_add_incoming.transfer_details,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+
+ if (NULL == transfer_details)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse transfer details `%s' at %u\n",
+ cmd->details.admin_add_incoming.transfer_details,
+ is->ip);
+ fail (is);
+ return;
+ }
+
cmd->details.admin_add_incoming.aih
= TALER_EXCHANGE_admin_add_incoming (exchange,
&reserve_pub,
&amount,
execution_date,
- wire,
+ sender_details,
+ transfer_details,
&add_incoming_cb,
is);
+ json_decref (sender_details);
+ json_decref (transfer_details);
if (NULL == cmd->details.admin_add_incoming.aih)
{
GNUNET_break (0);
@@ -1024,8 +1091,10 @@ interpreter_run (void *cls)
}
GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
- cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key
- = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key));
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &cmd->details.reserve_withdraw.blinding_key,
+ sizeof (cmd->details.reserve_withdraw.blinding_key));
+
cmd->details.reserve_withdraw.wsh
= TALER_EXCHANGE_reserve_withdraw (exchange,
cmd->details.reserve_withdraw.pk,
@@ -1194,6 +1263,14 @@ interpreter_run (void *cls)
return;
}
return;
+ case OC_TRACK_DEPOSIT:
+ TALER_MERCHANT_track_deposit (ctx,
+ MERCHANT_URI "/track/deposit",
+ cmd->details.track_deposit.wtid,
+ EXCHANGE_URI,
+ track_deposit_cb,
+ is);
+ return;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@@ -1285,11 +1362,6 @@ do_shutdown (void *cls)
GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature);
cmd->details.reserve_withdraw.sig.rsa_signature = NULL;
}
- if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key)
- {
- GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key);
- cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL;
- }
break;
case OC_CONTRACT:
if (NULL != cmd->details.contract.co)
@@ -1318,9 +1390,17 @@ do_shutdown (void *cls)
cmd->details.pay.ph = NULL;
}
break;
+ case OC_TRACK_DEPOSIT:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down /track/deposit\n");
+ if (NULL != cmd->details.track_deposit.tdo)
+ {
+ TALER_MERCHANT_track_deposit_cancel (cmd->details.track_deposit.tdo);
+ cmd->details.track_deposit.tdo = NULL;
+ }
+ break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unknown instruction %d at %u (%s)\n",
+ "Shutdown: unknown instruction %d at %u (%s)\n",
cmd->oc,
i,
cmd->label);
@@ -1398,11 +1478,20 @@ run (void *cls)
struct InterpreterState *is;
static struct Command commands[] =
{
+
+ { .oc = OC_TRACK_DEPOSIT,
+ .label = "track-deposit-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.track_deposit.wtid = "TESTWTID"},
+
+ { .oc = OC_END },
+
/* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
{ .oc = OC_ADMIN_ADD_INCOMING,
.label = "create-reserve-1",
.expected_response_code = MHD_HTTP_OK,
- .details.admin_add_incoming.wire = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62, \"uuid\":1 }",
+ .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62, \"uuid\":1 }",
+ .details.admin_add_incoming.transfer_details = "{ \"uuid\": 1}",
.details.admin_add_incoming.amount = "EUR:5.01" },
/* Withdraw a 5 EUR coin, at fee of 1 ct */
{ .oc = OC_WITHDRAW_SIGN,
@@ -1449,14 +1538,16 @@ run (void *cls)
{ .oc = OC_ADMIN_ADD_INCOMING,
.label = "create-reserve-2",
.expected_response_code = MHD_HTTP_OK,
- .details.admin_add_incoming.wire = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":63, \"uuid\":2 }",
+ .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":63, \"uuid\":2 }",
+ .details.admin_add_incoming.transfer_details = "{ \"uuid\": 2}",
.details.admin_add_incoming.amount = "EUR:1" },
/* Add another 4.01 EUR to reserve #2 */
{ .oc = OC_ADMIN_ADD_INCOMING,
.label = "create-reserve-2b",
.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.reserve_reference = "create-reserve-2",
- .details.admin_add_incoming.wire = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":63, \"uuid\":3 }",
+ .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":63, \"uuid\":3 }",
+ .details.admin_add_incoming.transfer_details = "{ \"uuid\": 3}",
.details.admin_add_incoming.amount = "EUR:4.01" },
/* Withdraw a 5 EUR coin, at fee of 1 ct */
@@ -1549,6 +1640,12 @@ main (int argc,
"taler-exchange-keyup",
"-c", "test_merchant_api.conf",
NULL);
+ if (NULL == proc)
+ {
+ fprintf (stderr,
+ "Failed to run taler-exchange-keyup. Check your PATH.\n");
+ return 77;
+ }
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
proc = GNUNET_OS_start_process (GNUNET_NO,
@@ -1557,7 +1654,14 @@ main (int argc,
"taler-exchange-dbinit",
"taler-exchange-dbinit",
"-c", "test_merchant_api.conf",
+ "-r",
NULL);
+ if (NULL == proc)
+ {
+ fprintf (stderr,
+ "Failed to run taler-exchange-dbinit. Check your PATH.\n");
+ return 77;
+ }
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
exchanged = GNUNET_OS_start_process (GNUNET_NO,
@@ -1567,9 +1671,15 @@ main (int argc,
"taler-exchange-httpd",
"-c", "test_merchant_api.conf",
NULL);
+ if (NULL == exchanged)
+ {
+ fprintf (stderr,
+ "Failed to run taler-exchange-httpd. Check your PATH.\n");
+ return 77;
+ }
/* give child time to start and bind against the socket */
fprintf (stderr,
- "Waiting for taler-exchange-httpd to be ready");
+ "Waiting for taler-exchange-httpd to be ready\n");
cnt = 0;
do
{
@@ -1596,9 +1706,19 @@ main (int argc,
"taler-merchant-httpd",
"-c", "test_merchant_api.conf",
NULL);
+ if (NULL == merchantd)
+ {
+ fprintf (stderr,
+ "Failed to run taler-merchant-httpd. Check your PATH.\n");
+ GNUNET_OS_process_kill (exchanged,
+ SIGKILL);
+ GNUNET_OS_process_wait (exchanged);
+ GNUNET_OS_process_destroy (exchanged);
+ return 77;
+ }
/* give child time to start and bind against the socket */
fprintf (stderr,
- "Waiting for taler-merchant-httpd to be ready");
+ "Waiting for taler-merchant-httpd to be ready\n");
cnt = 0;
do
{
@@ -1613,6 +1733,10 @@ main (int argc,
SIGKILL);
GNUNET_OS_process_wait (merchantd);
GNUNET_OS_process_destroy (merchantd);
+ GNUNET_OS_process_kill (exchanged,
+ SIGKILL);
+ GNUNET_OS_process_wait (exchanged);
+ GNUNET_OS_process_destroy (exchanged);
return 77;
}
}
diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c
@@ -65,7 +65,7 @@ extern uint32_t
TALER_MERCHANTDB_contract_get_values (PGconn *conn,
const struct GNUNET_HashCode *h_contract,
uint64_t *nounce,
- struct GNUNET_TIME_Absolute *edate);
+ struct GNUNET_TIME_Absolute *wire_transfer_deadline);
/**
@@ -104,7 +104,7 @@ run (void *cls, char *const *args, const char *cfgfile,
int64_t t_id;
int64_t p_id;
struct Contract contract;
- struct GNUNET_TIME_Absolute edate;
+ struct GNUNET_TIME_Absolute wire_transfer_deadline;
struct GNUNET_TIME_Absolute now;
uint64_t nounce;
struct GNUNET_HashCode h_contract_str;
@@ -294,17 +294,21 @@ run (void *cls, char *const *args, const char *cfgfile,
printf ("contract string : %s\n", aa);
GNUNET_CRYPTO_hash (aa, strlen (aa) + 1, &h_contract_str);
- if (GNUNET_SYSERR == TALER_MERCHANTDB_contract_get_values (db_conn, &h_contract_str, &nounce, &edate))
+ if (GNUNET_SYSERR ==
+ TALER_MERCHANTDB_contract_get_values (db_conn,
+ &h_contract_str,
+ &nounce,
+ &wire_transfer_deadline))
+ {
printf ("no hash found\n");
+ }
else
{
- fancy_time = GNUNET_STRINGS_absolute_time_to_string (edate);
+ fancy_time = GNUNET_STRINGS_absolute_time_to_string (wire_transfer_deadline);
printf ("hash found!, nounce is : %llu\n", nounce);
- printf ("hash found!, time is : %s\n", fancy_time);
+ printf ("hash found!, wire transfer time is : %s\n", fancy_time);
}
-
- return;
}