commit ff8fdfa0ec4f617993d385bb4258fb0538156c20
parent 500b6456966cdcce2ed10a92870f5a1c82e34168
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 15 Oct 2016 03:03:57 +0200
fixing #4577, as well as a few other cases where we need to index by merchant_pub as well as transaction ID; changes APIs in a few places
Diffstat:
9 files changed, 658 insertions(+), 460 deletions(-)
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -237,7 +237,7 @@ url_handler (void *cls,
/**
* Callback that frees all the elements in the hashmap
- *
+ *
* @param cls closure
* @param key current key
* @param value current value
@@ -564,6 +564,32 @@ instances_iterator_cb (void *cls,
}
}
+
+/**
+ * Lookup a merchant instance by its name.
+ *
+ * @param name name of the instance to resolve
+ * @return NULL if that instance is unknown to us
+ */
+struct MerchantInstance *
+TMH_lookup_instance (const char *name)
+{
+ struct GNUNET_HashCode h_receiver;
+
+ GNUNET_CRYPTO_hash (name,
+ strlen (name),
+ &h_receiver);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Looking for by-id key %s of '%s' in hashmap\n",
+ GNUNET_h2s (&h_receiver),
+ name);
+ /* We're fine if that returns NULL, the calling routine knows how
+ to handle that */
+ return GNUNET_CONTAINER_multihashmap_get (by_id_map,
+ &h_receiver);
+}
+
+
/**
* Extract merchant instance from the given JSON
*
@@ -582,29 +608,15 @@ get_instance (struct json_t *json)
{
struct json_t *receiver;
const char *receiver_str;
- struct GNUNET_HashCode h_receiver;
- struct MerchantInstance *ret;
- /*FIXME who decrefs receiver?*/
if (NULL == (receiver = json_object_get (json, "receiver")))
- receiver = json_string ("default");
-
- receiver_str = json_string_value (receiver);
- GNUNET_CRYPTO_hash (receiver_str,
- strlen (receiver_str),
- &h_receiver);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Looking for by-id key %s of '%s' in hashmap\n",
- GNUNET_h2s (&h_receiver),
- receiver_str);
- /* We're fine if that returns NULL, the calling routine knows how
- to handle that */
- ret = GNUNET_CONTAINER_multihashmap_get (by_id_map,
- &h_receiver);
- GNUNET_break (NULL != ret);
- return ret;
+ receiver_str = "default";
+ else
+ receiver_str = json_string_value (receiver);
+ return TMH_lookup_instance (receiver_str);
}
+
/**
* Iterate over each merchant instance, in order to populate
* each instance's own data
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
@@ -33,6 +33,7 @@
if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
} while (0)
+
/**
* Used by the iterator of the various merchant's instances given
* in configuration
@@ -42,7 +43,7 @@ struct IterateInstancesCls {
/**
* Handle for the configuration beig parsed
*/
- const struct GNUNET_CONFIGURATION_Handle *config;
+ const struct GNUNET_CONFIGURATION_Handle *config;
/**
* Current index in the global array of #MerchantInstance
@@ -54,7 +55,7 @@ struct IterateInstancesCls {
/**
* Flag indicating whether config contains a default instance
*/
- unsigned int default_instance;
+ unsigned int default_instance;
/**
* Wire plugin
@@ -92,7 +93,7 @@ struct MerchantInstance {
* Wire details for this instance
*/
struct json_t *j_wire;
-
+
/**
* Hash of our wire format details as given in #j_wire.
*/
@@ -102,7 +103,7 @@ struct MerchantInstance {
* Merchant's private key
*/
struct TALER_MerchantPrivateKeyP privkey;
-
+
/**
* Merchant's public key
*/
@@ -255,4 +256,14 @@ extern struct GNUNET_TIME_Relative wire_transfer_delay;
void
TMH_trigger_daemon (void);
+/**
+ * Lookup a merchant instance by its name.
+ *
+ * @param name name of the instance to resolve
+ * @return NULL if that instance is unknown to us
+ */
+struct MerchantInstance *
+TMH_lookup_instance (const char *name);
+
+
#endif
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
@@ -409,7 +409,7 @@ deposit_cb (void *cls,
if (0 != pc->pending)
return; /* still more to do */
-
+
mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
mr.purpose.size = htonl (sizeof (mr));
mr.h_contract = pc->h_contract;
@@ -796,326 +796,328 @@ check_coin_paid (void *cls,
* @param total_amount total amount we receive for the contract after fees
*/
static void
- check_transaction_exists (void *cls,
- uint64_t transaction_id,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const char *exchange_uri,
- const struct GNUNET_HashCode *h_contract,
- const struct GNUNET_HashCode *h_xwire,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund,
- const struct TALER_Amount *total_amount)
- {
- struct PayContext *pc = cls;
-
- if ( (0 == memcmp (h_contract,
- &pc->h_contract,
- sizeof (struct GNUNET_HashCode))) &&
- (0 == memcmp (h_xwire,
- &pc->mi->h_wire,
- sizeof (struct GNUNET_HashCode))) &&
- (timestamp.abs_value_us == pc->timestamp.abs_value_us) &&
- (refund.abs_value_us == pc->refund_deadline.abs_value_us) &&
- (0 == TALER_amount_cmp (total_amount,
- &pc->amount) ) )
- {
- pc->transaction_exits = GNUNET_YES;
- }
- else
- {
- GNUNET_break_op (0);
- pc->transaction_exits = GNUNET_SYSERR;
- }
- }
-
- extern struct MerchantInstance *
- get_instance (struct json_t *json);
-
- /**
- * Accomplish this payment.
- *
- * @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_pay (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
- {
- struct PayContext *pc;
- int res;
- json_t *root;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "In handler for /pay.\n");
- if (NULL == *connection_cls)
- {
- pc = GNUNET_new (struct PayContext);
- pc->hc.cc = &pay_context_cleanup;
- pc->connection = connection;
- *connection_cls = pc;
- }
- else
- {
- /* not the first call, recover state */
- pc = *connection_cls;
- }
- if (0 != pc->response_code)
- {
- /* We are *done* processing the request, just queue the response (!) */
- if (UINT_MAX == pc->response_code)
- {
- GNUNET_break (0);
- return MHD_NO; /* hard error */
- }
- res = MHD_queue_response (connection,
- pc->response_code,
- pc->response);
- if (NULL != pc->response)
- {
- MHD_destroy_response (pc->response);
- pc->response = NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Queueing response (%u) for /pay (%s).\n",
- (unsigned int) pc->response_code,
- res ? "OK" : "FAILED");
- return res;
- }
- if (NULL != pc->chosen_exchange)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Shouldn't be here. Old MHD version?\n");
- return MHD_YES;
- }
- res = TMH_PARSE_post_json (connection,
- &pc->json_parse_context,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- {
- GNUNET_break (0);
- return TMH_RESPONSE_reply_external_error (connection,
- "failed to parse JSON body");
- }
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES; /* the POST's body has to be further fetched */
-
- mi = get_instance (root);
-
- /* Got the JSON upload, parse it */
- {
- json_t *coins;
- json_t *coin;
- unsigned int coins_index;
- struct TALER_MerchantSignatureP merchant_sig;
- struct TALER_ContractPS cp;
- const char *chosen_exchange;
- 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_contract),
- 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 ("timestamp", &pc->timestamp),
- GNUNET_JSON_spec_uint64 ("transaction_id", &pc->transaction_id),
- GNUNET_JSON_spec_end()
- };
-
- res = TMH_PARSE_json_data (connection,
- root,
- spec);
- if (GNUNET_YES != res)
- {
- json_decref (root);
- GNUNET_break (0);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- pc->mi = get_instance (root);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "/pay: picked instance %s\n",
- pc->mi->id);
-
- if (NULL == pc->mi)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Not able to find the specified receiver\n");
- json_decref (root);
- return TMH_RESPONSE_reply_external_error (connection,
- "Unknown receiver given");
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "The receiver for this deposit is '%s', whose bank details are '%s'\n",
- pc->mi->id,
- json_dumps (pc->mi->j_wire, JSON_COMPACT));
- pc->chosen_exchange = GNUNET_strdup (chosen_exchange);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Parsed JSON for /pay.\n");
- cp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- cp.purpose.size = htonl (sizeof (struct TALER_ContractPS));
- cp.transaction_id = GNUNET_htonll (pc->transaction_id);
- TALER_amount_hton (&cp.total_amount,
- &pc->amount);
- TALER_amount_hton (&cp.max_fee,
- &pc->max_fee);
- cp.h_contract = pc->h_contract;
- cp.merchant_pub = pc->mi->pubkey;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_CONTRACT,
- &cp.purpose,
- &merchant_sig.eddsa_sig,
- &pc->mi->pubkey.eddsa_pub))
- {
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- return TMH_RESPONSE_reply_external_error (connection,
- "invalid merchant signature supplied");
- }
-
- /* '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->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 ("wire_transfer_deadline",
- &pc->wire_transfer_deadline),
- GNUNET_JSON_spec_end()
- };
-
- res = TMH_PARSE_json_data (connection,
- root,
- espec);
- if (GNUNET_YES != res)
- {
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- 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);
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- return TMH_RESPONSE_reply_external_error (connection,
- "refund deadline after wire transfer deadline");
- }
- }
-
-
- pc->coins_cnt = json_array_size (coins);
- if (0 == pc->coins_cnt)
- {
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- return TMH_RESPONSE_reply_external_error (connection,
- "no coins given");
- }
- /* note: 1 coin = 1 deposit confirmation expected */
- pc->dc = GNUNET_new_array (pc->coins_cnt,
- struct DepositConfirmation);
-
- /* This loop populates the array 'dc' in 'pc' */
- json_array_foreach (coins, coins_index, coin)
- {
- struct DepositConfirmation *dc = &pc->dc[coins_index];
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_denomination_public_key ("denom_pub", &dc->denom),
- TALER_JSON_spec_amount ("f" /* FIXME */, &dc->amount_with_fee),
- GNUNET_JSON_spec_fixed_auto ("coin_pub", &dc->coin_pub),
- TALER_JSON_spec_denomination_signature ("ub_sig", &dc->ub_sig),
- GNUNET_JSON_spec_fixed_auto ("coin_sig", &dc->coin_sig),
- GNUNET_JSON_spec_end()
- };
-
- res = TMH_PARSE_json_data (connection,
- coin,
- spec);
- if (GNUNET_YES != res)
- {
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- GNUNET_break (0);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
-
- {
- char *s;
-
- s = TALER_amount_to_string (&dc->amount_with_fee);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Coin #%i has f %s\n",
- coins_index,
- s);
- GNUNET_free (s);
- }
-
- dc->index = coins_index;
- dc->pc = pc;
- }
- GNUNET_JSON_parse_free (spec);
- } /* end of parsing of JSON upload */
- pc->pending = pc->coins_cnt;
-
- /* Check if this payment attempt has already succeeded */
- if (GNUNET_SYSERR ==
- db->find_payments_by_id (db->cls,
- pc->transaction_id,
- &check_coin_paid,
- pc))
- {
- GNUNET_break (0);
- json_decref (root);
- return TMH_RESPONSE_reply_internal_error (connection,
- "Merchant database error");
- }
- if (0 == pc->pending)
- {
- struct MHD_Response *resp;
- int ret;
-
- /* Payment succeeded in the past; take short cut
- and accept immediately */
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- json_decref (root);
- return ret;
- }
- /* Check if transaction is already known, if not store it. */
- if (GNUNET_SYSERR ==
- db->find_transaction (db->cls,
- pc->transaction_id,
- &pc->mi->pubkey,
- &check_transaction_exists,
+check_transaction_exists (void *cls,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const char *exchange_uri,
+ const struct GNUNET_HashCode *h_contract,
+ const struct GNUNET_HashCode *h_xwire,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund,
+ const struct TALER_Amount *total_amount)
+{
+ struct PayContext *pc = cls;
+
+ if ( (0 == memcmp (h_contract,
+ &pc->h_contract,
+ sizeof (struct GNUNET_HashCode))) &&
+ (0 == memcmp (h_xwire,
+ &pc->mi->h_wire,
+ sizeof (struct GNUNET_HashCode))) &&
+ (timestamp.abs_value_us == pc->timestamp.abs_value_us) &&
+ (refund.abs_value_us == pc->refund_deadline.abs_value_us) &&
+ (0 == TALER_amount_cmp (total_amount,
+ &pc->amount) ) )
+ {
+ pc->transaction_exits = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ pc->transaction_exits = GNUNET_SYSERR;
+ }
+}
+
+extern struct MerchantInstance *
+get_instance (struct json_t *json);
+
+
+/**
+ * Accomplish this payment.
+ *
+ * @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_pay (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct PayContext *pc;
+ int res;
+ json_t *root;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "In handler for /pay.\n");
+ if (NULL == *connection_cls)
+ {
+ pc = GNUNET_new (struct PayContext);
+ pc->hc.cc = &pay_context_cleanup;
+ pc->connection = connection;
+ *connection_cls = pc;
+ }
+ else
+ {
+ /* not the first call, recover state */
+ pc = *connection_cls;
+ }
+ if (0 != pc->response_code)
+ {
+ /* We are *done* processing the request, just queue the response (!) */
+ if (UINT_MAX == pc->response_code)
+ {
+ GNUNET_break (0);
+ return MHD_NO; /* hard error */
+ }
+ res = MHD_queue_response (connection,
+ pc->response_code,
+ pc->response);
+ if (NULL != pc->response)
+ {
+ MHD_destroy_response (pc->response);
+ pc->response = NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing response (%u) for /pay (%s).\n",
+ (unsigned int) pc->response_code,
+ res ? "OK" : "FAILED");
+ return res;
+ }
+ if (NULL != pc->chosen_exchange)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Shouldn't be here. Old MHD version?\n");
+ return MHD_YES;
+ }
+ res = TMH_PARSE_post_json (connection,
+ &pc->json_parse_context,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "failed to parse JSON body");
+ }
+ if ((GNUNET_NO == res) || (NULL == root))
+ return MHD_YES; /* the POST's body has to be further fetched */
+
+ mi = get_instance (root);
+
+ /* Got the JSON upload, parse it */
+ {
+ json_t *coins;
+ json_t *coin;
+ unsigned int coins_index;
+ struct TALER_MerchantSignatureP merchant_sig;
+ struct TALER_ContractPS cp;
+ const char *chosen_exchange;
+ 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_contract),
+ 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 ("timestamp", &pc->timestamp),
+ GNUNET_JSON_spec_uint64 ("transaction_id", &pc->transaction_id),
+ GNUNET_JSON_spec_end()
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_YES != res)
+ {
+ json_decref (root);
+ GNUNET_break (0);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ pc->mi = mi;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "/pay: picked instance %s\n",
+ pc->mi->id);
+
+ if (NULL == pc->mi)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Not able to find the specified receiver\n");
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "Unknown receiver given");
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "The receiver for this deposit is '%s', whose bank details are '%s'\n",
+ pc->mi->id,
+ json_dumps (pc->mi->j_wire, JSON_COMPACT));
+ pc->chosen_exchange = GNUNET_strdup (chosen_exchange);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Parsed JSON for /pay.\n");
+ cp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+ cp.purpose.size = htonl (sizeof (struct TALER_ContractPS));
+ cp.transaction_id = GNUNET_htonll (pc->transaction_id);
+ TALER_amount_hton (&cp.total_amount,
+ &pc->amount);
+ TALER_amount_hton (&cp.max_fee,
+ &pc->max_fee);
+ cp.h_contract = pc->h_contract;
+ cp.merchant_pub = pc->mi->pubkey;
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_CONTRACT,
+ &cp.purpose,
+ &merchant_sig.eddsa_sig,
+ &pc->mi->pubkey.eddsa_pub))
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "invalid merchant signature supplied");
+ }
+
+ /* '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->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 ("wire_transfer_deadline",
+ &pc->wire_transfer_deadline),
+ GNUNET_JSON_spec_end()
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ espec);
+ if (GNUNET_YES != res)
+ {
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ 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);
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "refund deadline after wire transfer deadline");
+ }
+ }
+
+
+ pc->coins_cnt = json_array_size (coins);
+ if (0 == pc->coins_cnt)
+ {
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "no coins given");
+ }
+ /* note: 1 coin = 1 deposit confirmation expected */
+ pc->dc = GNUNET_new_array (pc->coins_cnt,
+ struct DepositConfirmation);
+
+ /* This loop populates the array 'dc' in 'pc' */
+ json_array_foreach (coins, coins_index, coin)
+ {
+ struct DepositConfirmation *dc = &pc->dc[coins_index];
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_denomination_public_key ("denom_pub", &dc->denom),
+ TALER_JSON_spec_amount ("f" /* FIXME */, &dc->amount_with_fee),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub", &dc->coin_pub),
+ TALER_JSON_spec_denomination_signature ("ub_sig", &dc->ub_sig),
+ GNUNET_JSON_spec_fixed_auto ("coin_sig", &dc->coin_sig),
+ GNUNET_JSON_spec_end()
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ coin,
+ spec);
+ if (GNUNET_YES != res)
+ {
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ GNUNET_break (0);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+
+ {
+ char *s;
+
+ s = TALER_amount_to_string (&dc->amount_with_fee);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Coin #%i has f %s\n",
+ coins_index,
+ s);
+ GNUNET_free (s);
+ }
+
+ dc->index = coins_index;
+ dc->pc = pc;
+ }
+ GNUNET_JSON_parse_free (spec);
+ } /* end of parsing of JSON upload */
+ pc->pending = pc->coins_cnt;
+
+ /* Check if this payment attempt has already succeeded */
+ if (GNUNET_SYSERR ==
+ db->find_payments_by_id (db->cls,
+ pc->transaction_id,
+ &mi->pubkey,
+ &check_coin_paid,
+ pc))
+ {
+ GNUNET_break (0);
+ json_decref (root);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "Merchant database error");
+ }
+ if (0 == pc->pending)
+ {
+ struct MHD_Response *resp;
+ int ret;
+
+ /* Payment succeeded in the past; take short cut
+ and accept immediately */
+ resp = MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ json_decref (root);
+ return ret;
+ }
+ /* Check if transaction is already known, if not store it. */
+ if (GNUNET_SYSERR ==
+ db->find_transaction (db->cls,
+ pc->transaction_id,
+ &pc->mi->pubkey,
+ &check_transaction_exists,
pc))
{
GNUNET_break (0);
diff --git a/src/backend/taler-merchant-httpd_track-transaction.c b/src/backend/taler-merchant-httpd_track-transaction.c
@@ -411,18 +411,37 @@ wire_deposits_cb (void *cls,
/**
+ * Closure for #proof_cb().
+ */
+struct ProofCheckContext
+{
+ /**
+ * Proof returned from #proof_cb. The reference counter was
+ * increased for this reference and it must thus be freed.
+ * NULL if we did not find any proof. The JSON should
+ * match the `TrackTransferResponse` of the exchange API
+ * (https://api.taler.net/api-exchange.html#tracktransferresponse)
+ */
+ json_t *p_ret;
+
+};
+
+
+/**
* Function called with information about a wire transfer identifier.
* We actually never expect this to be called.
*
- * @param cls closure
+ * @param cls closure with a `struct ProofCheckContext`
* @param proof proof from exchange about what the wire transfer was for
*/
static void
proof_cb (void *cls,
const json_t *proof)
{
- /* FIXME #4577: store @a proof in @a cls to return with error message */
- GNUNET_break_op (0);
+ struct ProofCheckContext *pcc = cls;
+
+ GNUNET_break (NULL == pcc->p_ret);
+ pcc->p_ret = json_incref ((json_t *) proof);
}
@@ -437,7 +456,8 @@ proof_cb (void *cls,
* @param http_status HTTP status code we got, 0 on exchange protocol violation
* @param exchange_pub public key of the exchange used for signing @a json
* @param json original json reply (may include signatures, those have then been
- * validated already)
+ * validated already), should be a `TrackTransactionResponse`
+ * from the exchange API
* @param wtid wire transfer identifier used by the exchange, NULL if exchange did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
@@ -454,6 +474,7 @@ wtid_cb (void *cls,
{
struct TrackCoinContext *tcc = cls;
struct TrackTransactionContext *tctx = tcc->tctx;
+ struct ProofCheckContext pcc;
tcc->dwh = NULL;
if (MHD_HTTP_OK != http_status)
@@ -473,7 +494,7 @@ wtid_cb (void *cls,
}
tctx->current_wtid = *wtid;
tctx->current_execution_time = execution_time;
-
+ pcc.p_ret = NULL;
if (GNUNET_YES ==
db->find_proof_by_wtid (db->cls,
tctx->exchange_uri,
@@ -482,9 +503,15 @@ wtid_cb (void *cls,
NULL))
{
GNUNET_break_op (0);
- /* FIXME #4577: report error: we got this WTID before, and the
- transaction was NOT in the list. So exchange is lying to us!
- (or our DB is internally inconsistent.) */
+ resume_track_transaction_with_response
+ (tcc->tctx,
+ MHD_HTTP_CONFLICT,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:O, s:o, s:o}",
+ "error", "conflicting transfer data from exchange",
+ "transaction_tracking_claim", json,
+ "wtid_tracking_claim", pcc.p_ret,
+ "coin_pub", GNUNET_JSON_from_data_auto (&tcc->coin_pub)));
+ return;
}
tctx->wdh = TALER_EXCHANGE_track_transfer (tctx->eh,
wtid,
@@ -887,6 +914,7 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh,
}
ret = db->find_payments_by_id (db->cls,
transaction_id,
+ &tctx->mi->pubkey,
&coin_cb,
tctx);
if (GNUNET_SYSERR == ret)
diff --git a/src/backend/taler-merchant-httpd_track-transfer.c b/src/backend/taler-merchant-httpd_track-transfer.c
@@ -60,6 +60,11 @@ struct TrackTransferContext
struct TALER_EXCHANGE_TrackTransferHandle *wdh;
/**
+ * For which merchant instance is this tracking request?
+ */
+ struct MerchantInstance *mi;
+
+ /**
* HTTP connection we are handling.
*/
struct MHD_Connection *connection;
@@ -98,6 +103,16 @@ struct TrackTransferContext
struct TALER_WireTransferIdentifierRawP wtid;
/**
+ * Full original response we are currently processing.
+ */
+ const json_t *original_response;
+
+ /**
+ * Which transaction detail are we currently looking at?
+ */
+ unsigned int current_offset;
+
+ /**
* Response code to return.
*/
unsigned int response_code;
@@ -195,7 +210,7 @@ track_transfer_cleanup (struct TM_HandlerContext *hc)
* @param transaction_id of the contract
* @param coin_pub public key of the coin
* @param amount_with_fee amount the exchange will transfer for this coin
- * @param transfer_fee fee the exchange will charge for this coin
+ * @param deposit_fee fee the exchange will charge for this coin
* @param exchange_proof proof from exchange that coin was accepted
*/
static void
@@ -203,25 +218,34 @@ check_transfer (void *cls,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
- const struct TALER_Amount *transfer_fee,
+ const struct TALER_Amount *deposit_fee,
const json_t *exchange_proof)
{
struct TrackTransferContext *rctx = cls;
- const struct TALER_TrackTransferDetails *wdd = rctx->current_detail;
+ const struct TALER_TrackTransferDetails *ttd = rctx->current_detail;
- if (0 != memcmp (&wdd->coin_pub,
- coin_pub,
- sizeof (struct TALER_CoinSpendPublicKeyP)))
- return; /* not the coin we're looking for */
+ if (GNUNET_SYSERR == rctx->check_transfer_result)
+ return; /* already had a serious issue; odd that we're called more than once as well... */
if ( (0 != TALER_amount_cmp (amount_with_fee,
- &wdd->coin_value)) ||
- (0 != TALER_amount_cmp (transfer_fee,
- &wdd->coin_fee)) )
+ &ttd->coin_value)) ||
+ (0 != TALER_amount_cmp (deposit_fee,
+ &ttd->coin_fee)) )
{
/* Disagreement between the exchange and us about how much this
coin is worth! */
GNUNET_break_op (0);
rctx->check_transfer_result = GNUNET_SYSERR;
+ /* Build the `TrackTransferConflictDetails` */
+ rctx->response
+ = TMH_RESPONSE_make_json_pack ("{s:s, s:O, s:I, s:O, s:o, s:I, s:o, s:o}",
+ "hint", "disagreement about deposit valuation",
+ "exchange_deposit_proof", exchange_proof,
+ "conflict_offset", (json_int_t) rctx->current_offset,
+ "exchange_transfer_proof", rctx->original_response,
+ "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
+ "transaction_id", (json_int_t) transaction_id,
+ "amount_with_fee", TALER_JSON_from_amount (amount_with_fee),
+ "deposit_fee", TALER_JSON_from_amount (deposit_fee));
return;
}
rctx->check_transfer_result = GNUNET_OK;
@@ -284,40 +308,73 @@ wire_transfer_cb (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to persist wire transfer proof in DB\n");
+ resume_track_transfer_with_response
+ (rctx,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_json_pack ("{s:s}",
+ "details", "failed to store response from exchange to local database"));
+ return;
}
-
+ rctx->original_response = json;
for (i=0;i<details_length;i++)
{
+ rctx->current_offset = i;
rctx->current_detail = &details[i];
rctx->check_transfer_result = GNUNET_NO;
- ret = db->find_payments_by_id (db->cls,
- details[i].transaction_id,
- &check_transfer,
- rctx);
+ ret = db->find_payments_by_id_and_coin (db->cls,
+ details[i].transaction_id,
+ &rctx->mi->pubkey,
+ &details[i].coin_pub,
+ &check_transfer,
+ rctx);
if (GNUNET_SYSERR == ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to verify existing payment data in DB\n");
+ "Failed to obtain existing payment data from DB\n");
+ resume_track_transfer_with_response
+ (rctx,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_json_pack ("{s:s}",
+ "details", "failed to obtain deposit data from local database"));
+ return;
}
- if ( (GNUNET_NO == ret) ||
- (GNUNET_NO == rctx->check_transfer_result) )
+ if (GNUNET_NO == ret)
{
+ /* The exchange says we made this deposit, but WE do not
+ recall making it! Well, let's say thanks and accept the
+ money! */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to find payment data in DB\n");
+ rctx->check_transfer_result = GNUNET_OK;
}
- if (GNUNET_SYSERR == rctx->check_transfer_result)
+ if (GNUNET_NO == rctx->check_transfer_result)
{
- /* #check_transfer() failed, do something! */
+ /* Internal error: how can we have called #check_transfer()
+ but still have no result? */
GNUNET_break (0);
- /* FIXME: generate nicer custom response */
resume_track_transfer_with_response
(rctx,
- MHD_HTTP_FAILED_DEPENDENCY,
- TMH_RESPONSE_make_json_pack ("{s:I, s:O}",
- "index", (json_int_t) i,
- "details", json));
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:s}",
+ "details", "internal logic error",
+ "line", (json_int_t) __LINE__,
+ "file", __FILE__));
return;
}
+ if (GNUNET_SYSERR == rctx->check_transfer_result)
+ {
+ /* #check_transfer() failed, report conflict! */
+ GNUNET_break_op (0);
+ GNUNET_assert (NULL != rctx->response);
+ resume_track_transfer_with_response
+ (rctx,
+ MHD_HTTP_CONFLICT,
+ rctx->response);
+ rctx->response = NULL;
+ return;
+ }
+ /* Response is consistent with the /deposit we made, remember
+ it for future reference */
ret = db->store_coin_to_transfer (db->cls,
details[i].transaction_id,
&details[i].coin_pub,
@@ -326,15 +383,19 @@ wire_transfer_cb (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to persist coin to wire transfer mapping in DB\n");
+ resume_track_transfer_with_response
+ (rctx,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_json_pack ("{s:s}",
+ "details", "failed to store response from exchange to local database"));
+ return;
}
}
- /* FIXME: might want a more custom response here... */
+ rctx->original_response = NULL;
resume_track_transfer_with_response
(rctx,
MHD_HTTP_OK,
- TMH_RESPONSE_make_json_pack ("{s:I, s:O}",
- "exchange_status", (json_int_t) http_status,
- "details", json));
+ TMH_RESPONSE_make_json (json));
}
@@ -355,9 +416,9 @@ process_track_transfer_with_exchange (void *cls,
rctx->fo = NULL;
rctx->eh = eh;
rctx->wdh = TALER_EXCHANGE_track_transfer (eh,
- &rctx->wtid,
- &wire_transfer_cb,
- rctx);
+ &rctx->wtid,
+ &wire_transfer_cb,
+ rctx);
if (NULL == rctx->wdh)
{
GNUNET_break (0);
@@ -400,7 +461,9 @@ handle_track_transfer_timeout (void *cls)
* Generate a response based on the given @a proof.
*
* @param cls closure
- * @param proof proof from exchange about what the wire transfer was for
+ * @param proof proof from exchange about what the wire transfer was for.
+ * should match the `TrackTransactionResponse` format
+ * of the exchange
*/
static void
proof_cb (void *cls,
@@ -409,10 +472,7 @@ proof_cb (void *cls,
struct TrackTransferContext *rctx = cls;
rctx->response_code = MHD_HTTP_OK;
- /* FIXME: might want a more custom response here... */
- rctx->response = TMH_RESPONSE_make_json_pack ("{s:I, s:O}",
- "exchange_status", (json_int_t) MHD_HTTP_OK,
- "details", proof);
+ rctx->response = TMH_RESPONSE_make_json (proof);
}
@@ -438,6 +498,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
struct TrackTransferContext *rctx;
const char *str;
const char *uri;
+ const char *receiver_str;
int ret;
if (NULL == *connection_cls)
@@ -492,6 +553,15 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
"exchange argument missing");
rctx->uri = GNUNET_strdup (uri);
+ receiver_str = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "instance");
+ if (NULL == receiver_str)
+ receiver_str = "default";
+ rctx->mi = TMH_lookup_instance (receiver_str);
+ if (NULL == rctx->mi)
+ return TMH_RESPONSE_reply_not_found (connection,
+ "instance unknown");
str = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"wtid");
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
@@ -306,8 +306,24 @@ postgres_initialize (void *cls)
",deposit_fee_curr"
",exchange_proof"
" FROM merchant_deposits"
- " WHERE transaction_id=$1",
- 1);
+ " WHERE transaction_id=$1"
+ " AND merchant_pub=$2",
+ 2);
+ PG_PREPARE (pg,
+ "find_deposits_by_tid_and_coin",
+ "SELECT"
+ " amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",deposit_fee_val"
+ ",deposit_fee_frac"
+ ",deposit_fee_curr"
+ ",exchange_proof"
+ " FROM merchant_deposits"
+ " WHERE transaction_id=$1"
+ " AND merchant_pub=$2"
+ " AND coin_pub=$3",
+ 3);
PG_PREPARE (pg,
"find_transfers_by_transaction_id",
"SELECT"
@@ -611,7 +627,7 @@ postgres_find_transactions_by_date (void *cls,
GNUNET_PQ_result_spec_uint64 ("transaction_id",
&transaction_id),
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &merchant_pub),
+ &merchant_pub),
GNUNET_PQ_result_spec_auto_from_type ("h_contract",
&h_contract),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
@@ -647,7 +663,7 @@ postgres_find_transactions_by_date (void *cls,
}
PQclear (result);
return n;
-}
+}
/**
* Find information about a transaction.
@@ -746,10 +762,11 @@ postgres_find_transaction (void *cls,
/**
- * Lookup information about coin payments by transaction ID.
+ * Lookup information about coin payments by transaction ID (and @a merchant_pub)
*
* @param cls closure
* @param transaction_id key for the search
+ * @param merchant_pub public key of the merchant
* @param cb function to call with payment data
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown,
@@ -758,6 +775,7 @@ postgres_find_transaction (void *cls,
static int
postgres_find_payments_by_id (void *cls,
uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
TALER_MERCHANTDB_CoinDepositCallback cb,
void *cb_cls)
{
@@ -767,6 +785,7 @@ postgres_find_payments_by_id (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&transaction_id),
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
GNUNET_PQ_query_param_end
};
result = GNUNET_PQ_exec_prepared (pg->conn,
@@ -829,6 +848,93 @@ postgres_find_payments_by_id (void *cls,
/**
+ * Lookup information about coin payments by transaction ID.
+ *
+ * @param cls closure
+ * @param transaction_id key for the search
+ * @param merchant_pub merchant's public key. It's AND'd with @a transaction_id
+ * in order to find the result.
+ * @param coin_pub public key to use for the search
+ * @param cb function to call with payment data
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown,
+ * #GNUNET_SYSERR on hard errors
+ */
+static int
+postgres_find_payments_by_id_and_coin (void *cls,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ TALER_MERCHANTDB_CoinDepositCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ PGresult *result;
+ unsigned int i;
+
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&transaction_id),
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+ result = GNUNET_PQ_exec_prepared (pg->conn,
+ "find_deposits_by_tid_and_coin",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ for (i=0;i<PQntuples (result);i++)
+ {
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount deposit_fee;
+ json_t *exchange_proof;
+
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &amount_with_fee),
+ TALER_PQ_result_spec_amount ("deposit_fee",
+ &deposit_fee),
+ TALER_PQ_result_spec_json ("exchange_proof",
+ &exchange_proof),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ cb (cb_cls,
+ transaction_id,
+ coin_pub,
+ &amount_with_fee,
+ &deposit_fee,
+ exchange_proof);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+ PQclear (result);
+ return GNUNET_OK;
+
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Lookup information about a transfer by @a transaction_id. Note
* that in theory there could be multiple wire transfers for a
* single @a transaction_id, as the transaction may have involved
@@ -1118,6 +1224,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->find_transaction = &postgres_find_transaction;
plugin->find_transactions_by_date = &postgres_find_transactions_by_date;
plugin->find_payments_by_id = &postgres_find_payments_by_id;
+ plugin->find_payments_by_id_and_coin = &postgres_find_payments_by_id_and_coin;
plugin->find_transfers_by_id = &postgres_find_transfers_by_id;
plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid;
plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid;
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
@@ -383,6 +383,7 @@ run (void *cls)
FAILIF (GNUNET_OK !=
plugin->find_payments_by_id (plugin->cls,
transaction_id,
+ &merchant_pub,
&deposit_cb,
NULL));
FAILIF (GNUNET_OK !=
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
@@ -64,7 +64,8 @@ typedef void
* @param coin_pub public key of the coin
* @param amount_with_fee amount the exchange will deposit for this coin
* @param deposit_fee fee the exchange will charge for this coin
- * @param exchange_proof proof from exchange that coin was accepted
+ * @param exchange_proof proof from exchange that coin was accepted,
+ * matches the `interface DepositSuccess` of the documentation.
*/
typedef void
(*TALER_MERCHANTDB_CoinDepositCallback)(void *cls,
@@ -257,7 +258,7 @@ struct TALER_MERCHANTDB_Plugin
*
* @param cls our plugin handle
* @param transaction_id the transaction id to search
- * @param merchant_pub merchant's public key. It's AND'd with transaction_id
+ * @param merchant_pub merchant's public key. It's AND'd with @a transaction_id
* in order to find the result.
* @param cb function to call with transaction data
* @param cb_cls closure for @a cb
@@ -276,6 +277,8 @@ struct TALER_MERCHANTDB_Plugin
*
* @param cls closure
* @param transaction_id key for the search
+ * @param merchant_pub merchant's public key. It's AND'd with @a transaction_id
+ * in order to find the result.
* @param cb function to call with payment data
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown,
@@ -284,11 +287,34 @@ struct TALER_MERCHANTDB_Plugin
int
(*find_payments_by_id) (void *cls,
uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
TALER_MERCHANTDB_CoinDepositCallback cb,
void *cb_cls);
/**
+ * Lookup information about coin payments by transaction ID and coin.
+ *
+ * @param cls closure
+ * @param transaction_id key for the search
+ * @param merchant_pub merchant's public key. It's AND'd with @a transaction_id
+ * in order to find the result.
+ * @param coin_pub public key to use for the search
+ * @param cb function to call with payment data
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown,
+ * #GNUNET_SYSERR on hard errors
+ */
+ int
+ (*find_payments_by_id_and_coin) (void *cls,
+ uint64_t transaction_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ TALER_MERCHANTDB_CoinDepositCallback cb,
+ void *cb_cls);
+
+
+ /**
* Lookup information about a transfer by @a transaction_id. Note
* that in theory there could be multiple wire transfers for a
* single @a transaction_id, as the transaction may have involved
diff --git a/src/lib/merchant_api_track_transfer.c b/src/lib/merchant_api_track_transfer.c
@@ -77,13 +77,13 @@ struct TALER_MERCHANT_TrackTransferHandle
* Any changes should likely be reflected there as well.
*
* @param wdh handle to the operation
- * @param json response we got as the 'details' from the exchange
+ * @param json response we got
* @return #GNUNET_OK if we are done and all is well,
* #GNUNET_SYSERR if the response was bogus
*/
static int
-parse_exchange_details_ok (struct TALER_MERCHANT_TrackTransferHandle *wdh,
- const json_t *json)
+check_track_transfer_response_ok (struct TALER_MERCHANT_TrackTransferHandle *wdh,
+ const json_t *json)
{
json_t *details_j;
struct GNUNET_HashCode h_wire;
@@ -146,71 +146,12 @@ parse_exchange_details_ok (struct TALER_MERCHANT_TrackTransferHandle *wdh,
details);
}
GNUNET_JSON_parse_free (inner_spec);
+ TALER_MERCHANT_track_transfer_cancel (wdh);
return GNUNET_OK;
}
/**
- * We got a #MHD_HTTP_OK response for the /track/transfer request.
- * Check that the response is well-formed and if it is, call the
- * callback. If not, return an error code.
- *
- * This code is very similar to
- * exchange_api_track_transfer.c::check_track_transfer_response_ok.
- * (Except we do not check the signature, as that was done by the
- * backend which we trust already.)
- * Any changes should likely be reflected there as well.
- *
- * @param wdh handle to the operation
- * @param json response we got
- * @return #GNUNET_OK if we are done and all is well,
- * #GNUNET_SYSERR if the response was bogus
- */
-static int
-check_track_transfer_response_ok (struct TALER_MERCHANT_TrackTransferHandle *wdh,
- const json_t *json)
-{
- int ret;
- json_t *inner_j;
- uint32_t exchange_status;
- struct GNUNET_JSON_Specification outer_spec[] = {
- GNUNET_JSON_spec_json ("details", &inner_j),
- GNUNET_JSON_spec_uint32 ("exchange_status", &exchange_status),
- GNUNET_JSON_spec_end()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- outer_spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- switch (exchange_status)
- {
- case MHD_HTTP_OK:
- ret = parse_exchange_details_ok (wdh,
- inner_j);
- break;
- /* FIXME: other acceptable exchange status codes to handle here?
- FIXME: implement proper way to pass exchange status vs. backend
- status code to application! */
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected exchange status code %u\n",
- (unsigned int) exchange_status);
- ret = GNUNET_SYSERR;
- break;
- }
- if (GNUNET_OK == ret)
- TALER_MERCHANT_track_transfer_cancel (wdh);
- GNUNET_JSON_parse_free (outer_spec);
- return ret;
-}
-
-
-/**
* Function called when we're done processing the
* HTTP /track/transfer request.
*