gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-exchange] branch master updated: address #5010 for /


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: address #5010 for /refund
Date: Mon, 19 Jun 2017 16:07:42 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 92e6744  address #5010 for /refund
92e6744 is described below

commit 92e6744ac032a3c4c4118ac6b251f769c5478aa6
Author: Christian Grothoff <address@hidden>
AuthorDate: Mon Jun 19 16:07:34 2017 +0200

    address #5010 for /refund
---
 src/auditor/taler-auditor.c                        |   9 +-
 src/exchange/taler-exchange-httpd_db.c             | 289 +--------
 src/exchange/taler-exchange-httpd_db.h             |  14 -
 src/exchange/taler-exchange-httpd_deposit.c        |   7 +-
 src/exchange/taler-exchange-httpd_refund.c         | 357 ++++++++++-
 src/exchange/taler-exchange-httpd_responses.c      |  88 +--
 src/exchange/taler-exchange-httpd_responses.h      |  51 +-
 src/exchangedb/perf_taler_exchangedb_interpreter.c |  11 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 688 +++++++++++----------
 src/exchangedb/test_exchangedb.c                   |  13 +-
 src/include/taler_exchangedb_plugin.h              |  19 +-
 11 files changed, 784 insertions(+), 762 deletions(-)

diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 0d7d0b0..21c9579 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -1864,12 +1864,15 @@ wire_transfer_information_cb (void *cls,
   struct TALER_Amount coin_value_without_fee;
   struct TALER_EXCHANGEDB_TransactionList *tl;
   const struct TALER_CoinPublicInfo *coin;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* Obtain coin's transaction history */
-  tl = edb->get_coin_transactions (edb->cls,
+  qs = edb->get_coin_transactions (edb->cls,
                                    esession,
-                                   coin_pub);
-  if (NULL == tl)
+                                   coin_pub,
+                                  &tl);
+  if ( (qs < 0) ||
+       (NULL == tl) )
   {
     wcc->ok = GNUNET_SYSERR;
     report_row_inconsistency ("aggregation",
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 298f6c3..9e95ff4 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -280,279 +280,6 @@ TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionLis
 
 
 /**
- * Execute a "/refund".  Returns a confirmation that the refund
- * was successful, or a failure if we are not aware of a matching
- * /deposit or if it is too late to do the refund.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
-                       const struct TALER_EXCHANGEDB_Refund *refund)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_EXCHANGEDB_TransactionList *tlp;
-  const struct TALER_EXCHANGEDB_Deposit *dep;
-  const struct TALER_EXCHANGEDB_Refund *ref;
-  struct TEH_KS_StateHandle *mks;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_Amount expect_fee;
-  enum GNUNET_DB_QueryStatus qs;
-  int ret;
-  int deposit_found;
-  int refund_found;
-  int done;
-  int fee_cmp;
-
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  dep = NULL;
-  ref = NULL;
-  START_TRANSACTION (session, connection);
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &refund->coin.coin_pub);
-  if (NULL == tl)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                             MHD_HTTP_NOT_FOUND,
-                                             TALER_EC_REFUND_COIN_NOT_FOUND);
-  }
-  deposit_found = GNUNET_NO;
-  refund_found = GNUNET_NO;
-  for (tlp = tl; NULL != tlp; tlp = tlp->next)
-  {
-    switch (tlp->type)
-    {
-    case TALER_EXCHANGEDB_TT_DEPOSIT:
-      if (GNUNET_NO == deposit_found)
-      {
-        if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.deposit->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) )
-        {
-          dep = tlp->details.deposit;
-          deposit_found = GNUNET_YES;
-          break;
-        }
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      /* Melts cannot be refunded, ignore here */
-      break;
-    case TALER_EXCHANGEDB_TT_REFUND:
-      if (GNUNET_NO == refund_found)
-      {
-        /* First, check if existing refund request is identical */
-        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.refund->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) &&
-             (tlp->details.refund->rtransaction_id == refund->rtransaction_id) 
)
-        {
-          ref = tlp->details.refund;
-          refund_found = GNUNET_YES;
-          break;
-        }
-        /* Second, check if existing refund request conflicts */
-        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.refund->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) &&
-             (tlp->details.refund->rtransaction_id != refund->rtransaction_id) 
)
-        {
-          GNUNET_break_op (0); /* conflicting refund found */
-          refund_found = GNUNET_SYSERR;
-          /* NOTE: Alternatively we could total up all existing
-             refunds and check if the sum still permits the
-             refund requested (thus allowing multiple, partial
-             refunds). Fow now, we keep it simple. */
-          break;
-        }
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_PAYBACK:
-      /* Paybacks cannot be refunded, ignore here */
-      break;
-    }
-  }
-  /* handle if deposit was NOT found */
-  if (GNUNET_NO == deposit_found)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_transaction_unknown (connection,
-                                                  
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
-  }
-  /* handle if conflicting refund found */
-  if (GNUNET_SYSERR == refund_found)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_refund_conflict (connection,
-                                              tl);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-  /* handle if identical refund found */
-  if (GNUNET_YES == refund_found)
-  {
-    /* /refund already done, simply re-transmit confirmation */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_refund_success (connection,
-                                             ref);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-
-  /* check currency is compatible */
-  if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund->refund_amount,
-                                   &dep->amount_with_fee)) ||
-       (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund->refund_fee,
-                                   &dep->deposit_fee)) )
-  {
-    GNUNET_break_op (0); /* currency missmatch */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_PRECONDITION_FAILED,
-                                             
TALER_EC_REFUND_CURRENCY_MISSMATCH);
-  }
-
-  /* check if we already send the money for the /deposit */
-  done = TEH_plugin->test_deposit_done (TEH_plugin->cls,
-                                        session,
-                                        dep);
-  if (GNUNET_SYSERR == done)
-  {
-    /* Internal error, we first had the deposit in the history,
-       but now it is gone? */
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             TALER_EC_REFUND_DB_INCONSISTENT,
-                                              "database inconsistent");
-  }
-  if (GNUNET_YES == done)
-  {
-    /* money was already transferred to merchant, can no longer refund */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_GONE,
-                                             
TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
-  }
-
-  /* check refund amount is sufficiently low */
-  if (1 == TALER_amount_cmp (&refund->refund_amount,
-                             &dep->amount_with_fee) )
-  {
-    GNUNET_break_op (0); /* cannot refund more than original value */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_PRECONDITION_FAILED,
-                                             
TALER_EC_REFUND_INSUFFICIENT_FUNDS);
-  }
-
-  /* Check refund fee matches fee of denomination key! */
-  mks = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (mks,
-                                        &dep->coin.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dki)
-  {
-    /* DKI not found, but we do have a coin with this DK in our database;
-       not good... */
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_KS_release (mks);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
-                                              "denomination key not found");
-  }
-  TALER_amount_ntoh (&expect_fee,
-                     &dki->issue.properties.fee_refund);
-  fee_cmp = TALER_amount_cmp (&refund->refund_fee,
-                              &expect_fee);
-  TEH_KS_release (mks);
-
-  if (-1 == fee_cmp)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          TALER_EC_REFUND_FEE_TOO_LOW,
-                                           "refund_fee");
-  }
-  if (1 == fee_cmp)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Refund fee proposed by merchant is higher than necessary.\n");
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-
-  /* Finally, store new refund data */
-  qs = TEH_plugin->insert_refund (TEH_plugin->cls,
-                                 session,
-                                 refund);
-  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-  {
-    TALER_LOG_WARNING ("Failed to store /refund information in database\n");
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFUND_STORE_DB_ERROR);
-  }
-  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-  {
-    /* FIXME: #5010: retry! */
-  }
-  
-  COMMIT_TRANSACTION (session, connection);
-
-  return TEH_RESPONSE_reply_refund_success (connection,
-                                            refund);
-}
-
-
-/**
  * Parse coin melt requests from a JSON object and write them to
  * the database.
  *
@@ -581,6 +308,7 @@ refresh_check_melt (struct MHD_Connection *connection,
   struct TALER_Amount coin_residual;
   struct TALER_Amount spent;
   int res;
+  enum GNUNET_DB_QueryStatus qs;
 
   dk = TEH_KS_denomination_key_lookup (key_state,
                                        &coin_details->coin_info.denom_pub,
@@ -597,9 +325,11 @@ refresh_check_melt (struct MHD_Connection *connection,
   /* fee for THIS transaction; the melt amount includes the fee! */
   spent = coin_details->melt_amount_with_fee;
   /* add historic transaction costs of this coin */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
                                           session,
-                                          &coin_details->coin_info.coin_pub);
+                                          &coin_details->coin_info.coin_pub,
+                                         &tl);
+  (void) qs; /* FIXME #5010 */
   if (GNUNET_OK !=
       TEH_DB_calculate_transaction_list_totals (tl,
                                                &spent,
@@ -1921,7 +1651,8 @@ TEH_DB_execute_payback (struct MHD_Connection *connection,
   struct TALER_Amount amount;
   struct TALER_Amount spent;
   struct GNUNET_TIME_Absolute now;
-
+  enum GNUNET_DB_QueryStatus qs;
+  
   if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
   {
     GNUNET_break (0);
@@ -1955,9 +1686,11 @@ TEH_DB_execute_payback (struct MHD_Connection 
*connection,
   }
 
   /* Calculate remaining balance. */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
                                           session,
-                                          &coin->coin_pub);
+                                          &coin->coin_pub,
+                                         &tl);
+  (void) qs; /* FIXME #5010 */
   TALER_amount_get_zero (value->currency,
                          &spent);
   if (GNUNET_OK !=
diff --git a/src/exchange/taler-exchange-httpd_db.h 
b/src/exchange/taler-exchange-httpd_db.h
index e3717bd..5e2a27a 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -84,20 +84,6 @@ TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionLis
 
 
 /**
- * Execute a "/refund".  Returns a confirmation that the refund
- * was successful, or a failure if we are not aware of a matching
- * /deposit or if it is too late to do the refund.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
-                       const struct TALER_EXCHANGEDB_Refund *refund);
-
-
-/**
  * @brief Details about a melt operation of an individual coin.
  */
 struct TEH_DB_MeltDetails
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index c9c59ed..ccbd775 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -163,9 +163,12 @@ deposit_transaction (void *cls,
   /* Start with fee for THIS transaction */
   spent = deposit->amount_with_fee;
   /* add cost of all previous transactions */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
                                           session,
-                                          &deposit->coin.coin_pub);
+                                          &deposit->coin.coin_pub,
+                                         &tl);
+  if (0 > qs)
+    return qs;
   if (GNUNET_OK !=
       TEH_DB_calculate_transaction_list_totals (tl,
                                                &spent,
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index a5787ee..9846c73 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+  Copyright (C) 2014-2017 Inria and GNUnet e.V.
 
   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
@@ -37,6 +37,350 @@
 
 
 /**
+ * Generate successful refund confirmation message.
+ *
+ * @param connection connection to the client
+ * @param refund details about the successful refund
+ * @return MHD result code
+ */
+static int
+reply_refund_success (struct MHD_Connection *connection,
+                     const struct TALER_EXCHANGEDB_Refund *refund)
+{
+  struct TALER_RefundConfirmationPS rc;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
+  rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
+  rc.h_contract_terms = refund->h_contract_terms;
+  rc.coin_pub = refund->coin.coin_pub;
+  rc.merchant = refund->merchant_pub;
+  rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
+  TALER_amount_hton (&rc.refund_amount,
+                     &refund->refund_amount);
+  TALER_amount_hton (&rc.refund_fee,
+                     &refund->refund_fee);
+  TEH_KS_sign (&rc.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:s, s:o, s:o}",
+                                       "status", "REFUND_OK",
+                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
+                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
+}
+
+
+/**
+ * Generate generic refund failure message. All the details
+ * are in the @a response_code.  The body can be empty.
+ *
+ * @param connection connection to the client
+ * @param response_code response code to generate
+ * @param ec taler error code to include
+ * @return MHD result code
+ */
+static int
+reply_refund_failure (struct MHD_Connection *connection,
+                     unsigned int response_code,
+                     enum TALER_ErrorCode ec)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       response_code,
+                                       "{s:s, s:I}",
+                                       "status", "refund failure",
+                                      "code", (json_int_t) ec);
+}
+
+
+/**
+ * Generate refund conflict failure message. Returns the
+ * transaction list @a tl with the details about the conflict.
+ *
+ * @param connection connection to the client
+ * @param tl transaction list showing the conflict
+ * @return MHD result code
+ */
+static int
+reply_refund_conflict (struct MHD_Connection *connection,
+                      const struct TALER_EXCHANGEDB_TransactionList *tl)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_CONFLICT,
+                                       "{s:s, s:I, s:o}",
+                                       "status", "conflicting refund",
+                                      "code", (json_int_t) 
TALER_EC_REFUND_CONFLICT,
+                                       "history", 
TEH_RESPONSE_compile_transaction_history (tl));
+}
+
+
+/**
+ * Execute a "/refund" transaction.  Returns a confirmation that the
+ * refund was successful, or a failure if we are not aware of a
+ * matching /deposit or if it is too late to do the refund.
+ *
+ * IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure with a `const struct TALER_EXCHANGEDB_Refund *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refund_transaction (void *cls,
+                   struct MHD_Connection *connection,
+                   struct TALER_EXCHANGEDB_Session *session,
+                   int *mhd_ret)
+{
+  const struct TALER_EXCHANGEDB_Refund *refund = cls;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  const struct TALER_EXCHANGEDB_Deposit *dep;
+  const struct TALER_EXCHANGEDB_Refund *ref;
+  struct TEH_KS_StateHandle *mks;
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+  struct TALER_Amount expect_fee;
+  enum GNUNET_DB_QueryStatus qs;
+  int deposit_found;
+  int refund_found;
+  int fee_cmp;
+
+  dep = NULL;
+  ref = NULL;
+  tl = NULL;
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &refund->coin.coin_pub,
+                                         &tl);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = reply_refund_failure (connection,
+                                      MHD_HTTP_NOT_FOUND,
+                                      TALER_EC_REFUND_COIN_NOT_FOUND);
+    return qs;
+  }
+  deposit_found = GNUNET_NO;
+  refund_found = GNUNET_NO;
+  for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
+       NULL != tlp;
+       tlp = tlp->next)
+  {
+    switch (tlp->type)
+    {
+    case TALER_EXCHANGEDB_TT_DEPOSIT:
+      if (GNUNET_NO == deposit_found)
+      {
+        if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.deposit->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) )
+        {
+          dep = tlp->details.deposit;
+          deposit_found = GNUNET_YES;
+          break;
+        }
+      }
+      break;
+    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+      /* Melts cannot be refunded, ignore here */
+      break;
+    case TALER_EXCHANGEDB_TT_REFUND:
+      if (GNUNET_NO == refund_found)
+      {
+        /* First, check if existing refund request is identical */
+        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.refund->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) &&
+             (tlp->details.refund->rtransaction_id == refund->rtransaction_id) 
)
+        {
+          ref = tlp->details.refund;
+          refund_found = GNUNET_YES;
+          break;
+        }
+        /* Second, check if existing refund request conflicts */
+        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.refund->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) &&
+             (tlp->details.refund->rtransaction_id != refund->rtransaction_id) 
)
+        {
+          GNUNET_break_op (0); /* conflicting refund found */
+          refund_found = GNUNET_SYSERR;
+          /* NOTE: Alternatively we could total up all existing
+             refunds and check if the sum still permits the
+             refund requested (thus allowing multiple, partial
+             refunds). Fow now, we keep it simple. */
+          break;
+        }
+      }
+      break;
+    case TALER_EXCHANGEDB_TT_PAYBACK:
+      /* Paybacks cannot be refunded, ignore here */
+      break;
+    }
+  }
+  /* handle if deposit was NOT found */
+  if (GNUNET_NO == deposit_found)
+  {
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
+                                                      
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
+    return GNUNET_DB_STATUS_HARD_ERROR;    
+  }
+  /* handle if conflicting refund found */
+  if (GNUNET_SYSERR == refund_found)
+  {
+    *mhd_ret = reply_refund_conflict (connection,
+                                     tl);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR; 
+  }
+  /* handle if identical refund found */
+  if (GNUNET_YES == refund_found)
+  {
+    /* /refund already done, simply re-transmit confirmation */
+    *mhd_ret = reply_refund_success (connection,
+                                    ref);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check currency is compatible */
+  if ( (GNUNET_YES !=
+        TALER_amount_cmp_currency (&refund->refund_amount,
+                                   &dep->amount_with_fee)) ||
+       (GNUNET_YES !=
+        TALER_amount_cmp_currency (&refund->refund_fee,
+                                   &dep->deposit_fee)) )
+  {
+    GNUNET_break_op (0); /* currency missmatch */
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_PRECONDITION_FAILED,
+                                    TALER_EC_REFUND_CURRENCY_MISSMATCH);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check if we already send the money for the /deposit */
+  // FIXME: DB API...
+  qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
+                                     session,
+                                     dep);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+  {
+    /* Internal error, we first had the deposit in the history,
+       but now it is gone? */
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                 
TALER_EC_REFUND_DB_INCONSISTENT,
+                                                 "database inconsistent");
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    return qs; /* go and retry */
+  
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    /* money was already transferred to merchant, can no longer refund */
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_GONE,
+                                    TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check refund amount is sufficiently low */
+  if (1 == TALER_amount_cmp (&refund->refund_amount,
+                             &dep->amount_with_fee) )
+  {
+    GNUNET_break_op (0); /* cannot refund more than original value */
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_PRECONDITION_FAILED,
+                                    TALER_EC_REFUND_INSUFFICIENT_FUNDS);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  // FIXME: do this outside of transaction function?
+  /* Check refund fee matches fee of denomination key! */
+  mks = TEH_KS_acquire ();
+  dki = TEH_KS_denomination_key_lookup (mks,
+                                        &dep->coin.denom_pub,
+                                       TEH_KS_DKU_DEPOSIT);
+  if (NULL == dki)
+  {
+    /* DKI not found, but we do have a coin with this DK in our database;
+       not good... */
+    GNUNET_break (0);
+    TEH_KS_release (mks);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                 
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
+                                                 "denomination key not found");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  TALER_amount_ntoh (&expect_fee,
+                     &dki->issue.properties.fee_refund);
+  fee_cmp = TALER_amount_cmp (&refund->refund_fee,
+                              &expect_fee);
+  TEH_KS_release (mks);
+
+  if (-1 == fee_cmp)
+  {
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+                                              TALER_EC_REFUND_FEE_TOO_LOW,
+                                              "refund_fee");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (1 == fee_cmp)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Refund fee proposed by merchant is higher than necessary.\n");
+  }
+  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                          tl);
+
+  /* Finally, store new refund data */
+  qs = TEH_plugin->insert_refund (TEH_plugin->cls,
+                                 session,
+                                 refund);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+  {
+    TALER_LOG_WARNING ("Failed to store /refund information in database\n");
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFUND_STORE_DB_ERROR);
+    return qs;
+  }
+  /* Success or soft failure */
+  return qs;
+}
+
+
+/**
  * We have parsed the JSON information about the refund, do some basic
  * sanity checks (especially that the signature on the coin is valid)
  * and then execute the refund.  Note that we need the DB to check
@@ -51,6 +395,7 @@ verify_and_execute_refund (struct MHD_Connection *connection,
                           const struct TALER_EXCHANGEDB_Refund *refund)
 {
   struct TALER_RefundRequestPS rr;
+  int mhd_ret;
 
   rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
   rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
@@ -90,8 +435,14 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
                                                 
TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
                                                  "merchant_sig");
   }
-  return TEH_DB_execute_refund (connection,
-                               refund);
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &refund_transaction,
+                             (void *) refund))
+    return mhd_ret;
+  return reply_refund_success (connection,
+                              refund);
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 48df955..3f3f572 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -467,8 +467,8 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connection)
  * @param tl transaction history to JSON-ify
  * @return json representation of the @a rh
  */
-static json_t *
-compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_TransactionList *tl)
 {
   json_t *history;
 
@@ -663,7 +663,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct 
MHD_Connection *connection,
 {
   json_t *history;
 
-  history = compile_transaction_history (tl);
+  history = TEH_RESPONSE_compile_transaction_history (tl);
   if (NULL == history)
     return TEH_RESPONSE_reply_internal_error (connection,
                                               
TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
@@ -879,86 +879,6 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
 
 
 /**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
-                                    const struct 
TALER_EXCHANGEDB_TransactionList *tl)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_CONFLICT,
-                                       "{s:s, s:I, s:o}",
-                                       "status", "conflicting refund",
-                                      "code", (json_int_t) 
TALER_EC_REFUND_CONFLICT,
-                                       "history", compile_transaction_history 
(tl));
-}
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code.  The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec taler error code to include
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
-                                   unsigned int response_code,
-                                  enum TALER_ErrorCode ec)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       response_code,
-                                       "{s:s, s:I}",
-                                       "status", "refund failure",
-                                      "code", (json_int_t) ec);
-}
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
-                                   const struct TALER_EXCHANGEDB_Refund 
*refund)
-{
-  struct TALER_RefundConfirmationPS rc;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
-  rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
-  rc.h_contract_terms = refund->h_contract_terms;
-  rc.coin_pub = refund->coin.coin_pub;
-  rc.merchant = refund->merchant_pub;
-  rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
-  TALER_amount_hton (&rc.refund_amount,
-                     &refund->refund_amount);
-  TALER_amount_hton (&rc.refund_fee,
-                     &refund->refund_fee);
-  TEH_KS_sign (&rc.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s, s:o, s:o}",
-                                       "status", "REFUND_OK",
-                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
-                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
-}
-
-
-/**
  * Send a response for a failed "/refresh/melt" request.  The
  * transaction history of the given coin demonstrates that the
  * @a residual value of the coin is below the @a requested
@@ -983,7 +903,7 @@ TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct 
MHD_Connection *conne
 {
   json_t *history;
 
-  history = compile_transaction_history (tl);
+  history = TEH_RESPONSE_compile_transaction_history (tl);
   if (NULL == history)
     return TEH_RESPONSE_reply_internal_db_error (connection,
                                                 
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index aef5bf9..27f253b 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -274,46 +274,6 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct 
MHD_Connection *connection,
 
 
 /**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
-                                    const struct 
TALER_EXCHANGEDB_TransactionList *tl);
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code.  The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec error code uniquely identifying the error
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
-                                  unsigned int response_code,
-                                  enum TALER_ErrorCode ec);
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
-                                   const struct TALER_EXCHANGEDB_Refund 
*refund);
-
-
-/**
  * A merchant asked for details about a deposit, but
  * we do not know anything about the deposit. Generate the
  * 404 reply.
@@ -555,8 +515,19 @@ int
 TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection,
                                     const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
                                     const struct TALER_ReservePublicKeyP 
*reserve_pub,
+
                                     const struct TALER_Amount *amount,
                                     struct GNUNET_TIME_Absolute timestamp);
 
 
+/**
+ * Compile the transaction history of a coin into a JSON object.
+ *
+ * @param tl transaction history to JSON-ify
+ * @return json representation of the @a rh
+ */
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_TransactionList *tl);
+
+
 #endif
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c 
b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index 5e5c6a3..3f52e90 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1529,13 +1529,16 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           unsigned int coin_index;
           struct PERF_TALER_EXCHANGEDB_Coin *coin;
           struct TALER_EXCHANGEDB_TransactionList *transactions;
+         enum GNUNET_DB_QueryStatus qs;
 
           coin_index = 
state->cmd[state->i].details.get_coin_transaction.index_coin;
           coin = state->cmd[coin_index].exposed.data.coin;
-          transactions = state->plugin->get_coin_transactions 
(state->plugin->cls,
-                                                               state->session,
-                                                               
&coin->public_info.coin_pub);
-          GNUNET_assert (transactions != NULL);
+          qs = state->plugin->get_coin_transactions (state->plugin->cls,
+                                                    state->session,
+                                                    
&coin->public_info.coin_pub,
+                                                    &transactions);
+         GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+         GNUNET_assert (transactions != NULL);
           state->plugin->free_coin_transaction_list (state->plugin->cls,
                                                      transactions);
         }
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 41b5f72..9eec103 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2827,10 +2827,11 @@ postgres_mark_deposit_tiny (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to the database
  * @param deposit the deposit to check
- * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
- *         #GNUNET_SYSERR on error (deposit unknown)
+ * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+ *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+ *         otherwise transaction error status (incl. deposit unknown)
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_test_deposit_done (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2842,50 +2843,25 @@ postgres_test_deposit_done (void *cls,
     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "test_deposit_done",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (1 != PQntuples (result))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  {
-    uint8_t done = 0;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("done",
-                                            &done),
-      GNUNET_PQ_result_spec_end
-    };
+  uint8_t done = 0;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("done",
+                                         &done),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    PQclear (result);
-    return (done ? GNUNET_YES : GNUNET_NO);
-  }
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "test_deposit_done",
+                                                params,
+                                                rs);
+  if (qs < 0)
+    return qs;
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    return GNUNET_DB_STATUS_HARD_ERROR; /* deposit MUST exist */
+  return (done
+         ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
+         : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS);
 }
 
 
@@ -4071,317 +4047,385 @@ postgres_get_transfer (void *cls,
 
 
 /**
- * Compile a list of all (historic) transactions performed
- * with the given coin (/refresh/melt, /deposit and /refund operations).
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
+ * Closure for callbacks called from #postgres_get_coin_transactions()
  */
-static struct TALER_EXCHANGEDB_TransactionList *
-postgres_get_coin_transactions (void *cls,
-                                struct TALER_EXCHANGEDB_Session *session,
-                                const struct TALER_CoinSpendPublicKeyP 
*coin_pub)
+struct CoinHistoryContext
 {
+  /**
+   * Head of the coin's history list.
+   */
   struct TALER_EXCHANGEDB_TransactionList *head;
 
-  head = NULL;
-  /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+  /**
+   * Public key of the coin we are building the history for.
+   */
+  const struct TALER_CoinSpendPublicKeyP *coin_pub;
+
+  /**
+   * Closure for all callbacks of this database plugin.
+   */
+  void *db_cls;
+
+  /**
+   * Database session we are using.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+  
+  /**
+   * Set to #GNUNET_SYSERR on errors
+   */ 
+  int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_deposit (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Deposit *deposit;
     struct TALER_EXCHANGEDB_TransactionList *tl;
-
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_deposit_with_coin_pub",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      QUERY_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i = 0; i < nrows; i++)
+    
+    deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
     {
-      struct TALER_EXCHANGEDB_Deposit *deposit;
-
-      deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &deposit->amount_with_fee),
-          TALER_PQ_result_spec_amount ("fee_deposit",
-                                       &deposit->deposit_fee),
-          GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                               &deposit->timestamp),
-          GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
-                                               &deposit->refund_deadline),
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                                &deposit->merchant_pub),
-          GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                                &deposit->h_contract_terms),
-          GNUNET_PQ_result_spec_auto_from_type ("h_wire",
-                                                &deposit->h_wire),
-          TALER_PQ_result_spec_json ("wire",
-                                     &deposit->receiver_wire_account),
-          GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                               &deposit->csig),
-          GNUNET_PQ_result_spec_end
-        };
-
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (deposit);
-          PQclear (result);
-          goto cleanup;
-        }
-        deposit->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
-      tl->details.deposit = deposit;
-      if (GNUNET_SYSERR == get_known_coin (cls,
-                                           session,
-                                           &deposit->coin.coin_pub,
-                                           &deposit->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &deposit->amount_with_fee),
+       TALER_PQ_result_spec_amount ("fee_deposit",
+                                    &deposit->deposit_fee),
+       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+                                            &deposit->timestamp),
+       GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+                                            &deposit->refund_deadline),
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                             &deposit->merchant_pub),
+       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                             &deposit->h_contract_terms),
+       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+                                             &deposit->h_wire),
+       TALER_PQ_result_spec_json ("wire",
+                                  &deposit->receiver_wire_account),
+       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                             &deposit->csig),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (deposit);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (deposit);
+       chc->status = GNUNET_SYSERR;
+       return;
       }
-      head = tl;
-      continue;
+      deposit->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
+    tl->details.deposit = deposit;
+    if (GNUNET_SYSERR == get_known_coin (chc->db_cls,
+                                        chc->session,
+                                        chc->coin_pub,
+                                        &deposit->coin))
+    {
+      GNUNET_break (0);
+      GNUNET_free (deposit);
+      chc->status = GNUNET_SYSERR;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_melt (void *cls,
+              PGresult *result,
+              unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (&coin_pub->eddsa_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_RefreshMelt *melt;
     struct TALER_EXCHANGEDB_TransactionList *tl;
 
-    /* check if the melt records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_refresh_session_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
+    melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
     {
-      struct TALER_EXCHANGEDB_RefreshMelt *melt;
-
-      melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          GNUNET_PQ_result_spec_auto_from_type ("session_hash",
-                                                &melt->session_hash),
-          /* oldcoin_index not needed */
-          GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                                &melt->coin_sig),
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &melt->amount_with_fee),
-          TALER_PQ_result_spec_amount ("fee_refresh",
-                                       &melt->melt_fee),
-          GNUNET_PQ_result_spec_end
-        };
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (melt);
-          PQclear (result);
-          goto cleanup;
-        }
-       melt->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
-      tl->details.melt = melt;
-      if (GNUNET_SYSERR == get_known_coin (cls,
-                                           session,
-                                           coin_pub,
-                                           &melt->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       GNUNET_PQ_result_spec_auto_from_type ("session_hash",
+                                             &melt->session_hash),
+       /* oldcoin_index not needed */
+       GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+                                             &melt->coin_sig),
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &melt->amount_with_fee),
+       TALER_PQ_result_spec_amount ("fee_refresh",
+                                    &melt->melt_fee),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (melt);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (melt);
+       chc->status = GNUNET_SYSERR;
+       return;
       }
-      head = tl;
-      continue;
+      melt->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
+    tl->details.melt = melt;
+    if (GNUNET_SYSERR == get_known_coin (chc->db_cls,
+                                        chc->session,
+                                        chc->coin_pub,
+                                        &melt->coin))
+    {
+      GNUNET_break (0);
+      GNUNET_free (melt);
+      chc->status = GNUNET_SYSERR;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_REFUND */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_refund (void *cls,
+                PGresult *result,
+                unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Refund *refund;
     struct TALER_EXCHANGEDB_TransactionList *tl;
 
-    /* check if a refund records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_refunds_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
+    refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
     {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
-    {
-      struct TALER_EXCHANGEDB_Refund *refund;
-
-      refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                                &refund->merchant_pub),
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
-                                                &refund->merchant_sig),
-          GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                                &refund->h_contract_terms),
-          GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
-                                        &refund->rtransaction_id),
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &refund->refund_amount),
-          TALER_PQ_result_spec_amount ("fee_refund",
-                                       &refund->refund_fee),
-          GNUNET_PQ_result_spec_end
-        };
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (refund);
-          PQclear (result);
-          goto cleanup;
-        }
-       refund->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_REFUND;
-      tl->details.refund = refund;
-      if (GNUNET_SYSERR ==
-          get_known_coin (cls,
-                          session,
-                          coin_pub,
-                          &refund->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                             &refund->merchant_pub),
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
+                                             &refund->merchant_sig),
+       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                             &refund->h_contract_terms),
+       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                     &refund->rtransaction_id),
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &refund->refund_amount),
+       TALER_PQ_result_spec_amount ("fee_refund",
+                                    &refund->refund_fee),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (refund);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (refund);
+       chc->status = GNUNET_SYSERR;
+       return;
       }
-      head = tl;
-      continue;
+      refund->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_REFUND;
+    tl->details.refund = refund;
+    if (GNUNET_SYSERR ==
+       get_known_coin (chc->db_cls,
+                       chc->session,
+                       chc->coin_pub,
+                       &refund->coin))
+    {
+      GNUNET_break (0);
+      GNUNET_free (refund);
+      chc->status = GNUNET_SYSERR;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_PAYBACK */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_payback (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Payback *payback;
     struct TALER_EXCHANGEDB_TransactionList *tl;
-
-    /* check if a refund records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "payback_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
+    
+    payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
     {
-      struct TALER_EXCHANGEDB_Payback *payback;
-
-      payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       TALER_PQ_result_spec_amount ("amount",
+                                    &payback->value),
+       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+                                             &payback->reserve_pub),
+       GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+                                             &payback->coin_blind),
+       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                             &payback->coin_sig),
+       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+                                            &payback->timestamp),
+       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+                                             
&payback->coin.denom_pub.rsa_public_key),
+       GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+                                            
&payback->coin.denom_sig.rsa_signature),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          TALER_PQ_result_spec_amount ("amount",
-                                       &payback->value),
-          GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
-                                                &payback->reserve_pub),
-          GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
-                                                &payback->coin_blind),
-          GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                                &payback->coin_sig),
-          GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                               &payback->timestamp),
-          GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-                                                
&payback->coin.denom_pub.rsa_public_key),
-          GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-                                               
&payback->coin.denom_sig.rsa_signature),
-          GNUNET_PQ_result_spec_end
-        };
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (payback);
-          PQclear (result);
-          goto cleanup;
-        }
-       payback->coin.coin_pub = *coin_pub;
+       GNUNET_break (0);
+       GNUNET_free (payback);
+       chc->status = GNUNET_SYSERR;
+       return;
       }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
-      tl->details.payback = payback;
-      head = tl;
-      continue;
+      payback->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
+    tl->details.payback = payback;
+    chc->head = tl;
   }
+}
 
-  return head;
- cleanup:
-  if (NULL != head)
-    common_free_coin_transaction_list (cls,
-                                       head);
-  return NULL;
+
+/**
+ * Compile a list of all (historic) transactions performed
+ * with the given coin (/refresh/melt, /deposit and /refund operations).
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param coin_pub coin to investigate
+ * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @return database transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_coin_transactions (void *cls,
+                                struct TALER_EXCHANGEDB_Session *session,
+                                const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                               struct TALER_EXCHANGEDB_TransactionList **tlp)
+{
+  struct CoinHistoryContext chc;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct {
+    /**
+     * SQL prepared statement name.
+     */
+    const char *statement;
+
+    /**
+     * Function to call to handle the result(s).
+     */
+    GNUNET_PQ_PostgresResultHandler cb;
+  } work[] = {
+    /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+    { "get_deposit_with_coin_pub",
+      &add_coin_deposit },
+    /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+    { "get_refresh_session_by_coin",
+      &add_coin_melt },
+    /** #TALER_EXCHANGEDB_TT_REFUND */
+    { "get_refunds_by_coin",
+      &add_coin_refund },
+    /** #TALER_EXCHANGEDB_TT_PAYBACK */
+    { "payback_by_coin",
+      &add_coin_payback },
+    { NULL, NULL }
+  };
+
+  chc.head = NULL;
+  chc.status = GNUNET_OK;
+  chc.coin_pub = coin_pub;
+  chc.session = session;
+  chc.db_cls = cls;
+  for (unsigned int i=0;NULL != work[i].statement; i++)
+  {
+    qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                              work[i].statement,
+                                              params,
+                                              work[i].cb,
+                                              &chc);
+    if ( (0 > qs) ||
+        (GNUNET_OK != chc.status) )
+    {
+      if (NULL != chc.head)
+       common_free_coin_transaction_list (cls,
+                                          chc.head);
+      *tlp = NULL;
+      if (GNUNET_OK != chc.status)
+       qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return qs;
+    }
+  }
+  *tlp = chc.head;
+  if (NULL == chc.head)
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index ac1301d..19e8caa 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -726,10 +726,13 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
   {
     /* Just to test fetching a coin with melt history */
     struct TALER_EXCHANGEDB_TransactionList *tl;
+    enum GNUNET_DB_QueryStatus qs;
 
-    tl = plugin->get_coin_transactions (plugin->cls,
+    qs = plugin->get_coin_transactions (plugin->cls,
                                         session,
-                                        &meltp->coin.coin_pub);
+                                        &meltp->coin.coin_pub,
+                                       &tl);
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
     plugin->free_coin_transaction_list (plugin->cls,
                                         tl);
   }
@@ -1930,9 +1933,11 @@ run (void *cls)
                                                  NULL));
 
   FAILIF (1 != auditor_row_cnt);
-  tl = plugin->get_coin_transactions (plugin->cls,
+  qs = plugin->get_coin_transactions (plugin->cls,
                                       session,
-                                      &refund.coin.coin_pub);
+                                      &refund.coin.coin_pub,
+                                     &tl);
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
   GNUNET_assert (NULL != tl);
   matched = 0;
   for (tlp = tl; NULL != tlp; tlp = tlp->next)
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index af6b554..8a20108 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1346,16 +1346,17 @@ struct TALER_EXCHANGEDB_Plugin
 
 
   /**
-   * Test if a deposit was marked as done, thereby declaring that it cannot be
-   * refunded anymore.
+   * Test if a deposit was marked as done, thereby declaring that it
+   * cannot be refunded anymore.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param deposit the deposit to check
-   * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
-   *         #GNUNET_SYSERR on error (deposit unknown)
+   * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+   *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+   *         otherwise transaction error status (incl. deposit unknown)
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*test_deposit_done) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1700,12 +1701,14 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database connection
    * @param coin_pub coin to investigate
-   * @return list of transactions, NULL if coin is fresh
+   * @param[out] tlp set to list of transactions, NULL if coin is fresh
+   * @return database transaction status
    */
-  struct TALER_EXCHANGEDB_TransactionList *
+  enum GNUNET_DB_QueryStatus
   (*get_coin_transactions) (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
-                            const struct TALER_CoinSpendPublicKeyP *coin_pub);
+                            const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           struct TALER_EXCHANGEDB_TransactionList **tlp);
 
 
   /**

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]