aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exchange/taler-exchange-httpd_db.c280
-rw-r--r--src/exchange/taler-exchange-httpd_db.h14
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c20
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c80
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h38
-rw-r--r--src/exchangedb/plugin_exchangedb_common.c5
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c40
-rw-r--r--src/include/taler_exchangedb_plugin.h42
8 files changed, 512 insertions, 7 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 237a1aa7c..499623e40 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -105,12 +105,16 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
{
struct TALER_Amount spent = *off;
struct TALER_EXCHANGEDB_TransactionList *pos;
+ struct TALER_Amount refunded;
+ TALER_amount_get_zero (spent.currency,
+ &refunded);
for (pos = tl; NULL != pos; pos = pos->next)
{
switch (pos->type)
{
case TALER_EXCHANGEDB_TT_DEPOSIT:
+ /* spent += pos->amount_with_fee */
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
@@ -121,6 +125,7 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
}
break;
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+ /* spent += pos->amount_with_fee */
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
@@ -130,8 +135,37 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
return GNUNET_SYSERR;
}
break;
+ case TALER_EXCHANGEDB_TT_REFUND:
+ /* refunded += pos->refund_amount - pos->refund_fee */
+ if (GNUNET_OK !=
+ TALER_amount_add (&refunded,
+ &refunded,
+ &pos->details.refund->refund_amount))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&refunded,
+ &refunded,
+ &pos->details.refund->refund_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ break;
}
}
+ /* spent = spent - refunded */
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&spent,
+ &spent,
+ &refunded))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
*ret = spent;
return GNUNET_OK;
}
@@ -262,6 +296,252 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
/**
+ * 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
+TMH_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 TMH_KS_StateHandle *mks;
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+ struct TALER_Amount expect_fee;
+ int ret;
+ int deposit_found;
+ int refund_found;
+ int done;
+ int fee_cmp;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ dep = NULL;
+ ref = NULL;
+ START_TRANSACTION (session, connection);
+ tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls,
+ session,
+ &refund->coin.coin_pub);
+ if (NULL == tl)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_refund_failure (connection,
+ MHD_HTTP_NOT_FOUND);
+ }
+ deposit_found = GNUNET_NO;
+ for (tlp = tl; NULL != tlp; tlp = tlp->next)
+ {
+ switch (tlp->type)
+ {
+ case TALER_EXCHANGEDB_TT_DEPOSIT:
+ {
+ dep = tlp->details.deposit;
+ if ( (0 == memcmp (&dep->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&dep->h_contract,
+ &refund->h_contract,
+ sizeof (struct GNUNET_HashCode))) &&
+ (dep->transaction_id == refund->transaction_id) )
+ {
+ deposit_found = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+ /* Melts cannot be refunded, ignore here */
+ break;
+ case TALER_EXCHANGEDB_TT_REFUND:
+ {
+ ref = tlp->details.refund;
+ /* First, check if existing refund request is identical */
+ if ( (0 == memcmp (&ref->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&ref->h_contract,
+ &refund->h_contract,
+ sizeof (struct GNUNET_HashCode))) &&
+ (ref->transaction_id == refund->transaction_id) &&
+ (ref->rtransaction_id == refund->rtransaction_id) )
+ {
+ refund_found = GNUNET_YES;
+ break;
+ }
+ /* Second, check if existing refund request conflicts */
+ if ( (0 == memcmp (&ref->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&ref->h_contract,
+ &refund->h_contract,
+ sizeof (struct GNUNET_HashCode))) &&
+ (ref->transaction_id == refund->transaction_id) &&
+ (ref->rtransaction_id != refund->rtransaction_id) )
+ {
+ GNUNET_break_op (0); /* conflicting refound 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;
+ }
+ }
+ /* handle if deposit was NOT found */
+ if (GNUNET_NO == deposit_found)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return TMH_RESPONSE_reply_deposit_unknown (connection);
+ }
+ /* handle if conflicting refund found */
+ if (GNUNET_SYSERR == refund_found)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ ret = TMH_RESPONSE_reply_refund_conflict (connection,
+ tl);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return ret;
+ }
+ /* handle if identical refund found */
+ if (GNUNET_YES == refund_found)
+ {
+ /* /refund already done, simply re-transmit confirmation */
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ ret = TMH_RESPONSE_reply_refund_success (connection,
+ ref);
+ TMH_plugin->free_coin_transaction_list (TMH_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 */
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_refund_failure (connection,
+ MHD_HTTP_PRECONDITION_FAILED);
+ }
+
+ /* check if we already send the money for the /deposit */
+ done = TMH_plugin->test_deposit_done (TMH_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);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "database inconsistent");
+ }
+ if (GNUNET_YES == done)
+ {
+ /* money was already transferred to merchant, can no longer refund */
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return TMH_RESPONSE_reply_refund_failure (connection,
+ MHD_HTTP_GONE);
+ }
+
+ /* We no longer need 'tl' or 'dep' or 'ref' */
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+
+ /* 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 */
+ return TMH_RESPONSE_reply_refund_failure (connection,
+ MHD_HTTP_PRECONDITION_FAILED);
+ }
+
+ /* Check refund fee matches fee of denomination key! */
+ mks = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (mks,
+ &dep->coin.denom_pub,
+ TMH_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);
+ TMH_KS_release (mks);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "denomination key not found");
+ }
+ TALER_amount_ntoh (&expect_fee,
+ &dki->issue.properties.fee_refund);
+ fee_cmp = TALER_amount_cmp (&refund->refund_fee,
+ &expect_fee);
+ TMH_KS_release (mks);
+
+ if (-1 == fee_cmp)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "refund_fee");
+ }
+ if (1 == fee_cmp)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Refund fee proposed by merchant is higher than necessary.\n");
+ }
+
+ /* Finally, store new refund data */
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refund (TMH_plugin->cls,
+ session,
+ refund))
+ {
+ TALER_LOG_WARNING ("Failed to store /refund information in database\n");
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ COMMIT_TRANSACTION(session, connection);
+
+ return TMH_RESPONSE_reply_refund_success (connection,
+ refund);
+}
+
+
+/**
* Execute a /reserve/status. Given the public key of a reserve,
* return the associated transaction history.
*
diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h
index d6245a702..7cf498285 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -41,6 +41,20 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
/**
+ * 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
+TMH_DB_execute_refund (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_Refund *refund);
+
+
+/**
* Execute a "/reserve/status". Given the public key of a reserve,
* return the associated transaction history.
*
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index c5f24000d..b6dc203c0 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -63,6 +63,21 @@ verify_and_execute_refund (struct MHD_Connection *connection,
&refund->refund_fee);
dr.merchant = refund->merchant_pub;
dr.coin_pub = refund->coin.coin_pub;
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&refund->refund_amount,
+ &refund->refund_fee) )
+ {
+ GNUNET_break_op (0);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "refund_fee");
+ }
+ if (-1 == TALER_amount_cmp (&refund->refund_amount,
+ &refund->refund_fee) )
+ {
+ GNUNET_break_op (0);
+ return TMH_RESPONSE_reply_signature_invalid (connection,
+ "refund_amount");
+ }
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&dr.purpose,
@@ -73,13 +88,8 @@ verify_and_execute_refund (struct MHD_Connection *connection,
return TMH_RESPONSE_reply_signature_invalid (connection,
"merchant_sig");
}
-#if 1
- GNUNET_break (0); // FIXME: not implemented
- return MHD_NO;
-#else
return TMH_DB_execute_refund (connection,
refund);
-#endif
}
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index c8a72f499..2011a5e49 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -643,6 +643,86 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
/**
+ * 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
+TMH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_TransactionList *tl)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_CONFLICT,
+ "{s:s, s:o}",
+ "status", "conflicting refund",
+ "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
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
+ unsigned int response_code)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ response_code,
+ "{s:s}",
+ "error",
+ "no details");
+}
+
+
+/**
+ * Generate successful refund confirmation message.
+ *
+ * @param connection connection to the client
+ * @param refund details about the successful refund
+ * @return MHD result code
+ */
+int
+TMH_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 = refund->h_contract;
+ rc.transaction_id = GNUNET_htonll (refund->transaction_id);
+ 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);
+ TMH_KS_sign (&rc.purpose,
+ &pub,
+ &sig);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s, s:o, s:o}",
+ "status", "REFUND_OK",
+ "sig", GNUNET_JSON_from_data (&sig,
+ sizeof (sig)),
+ "pub", GNUNET_JSON_from_data (&pub,
+ sizeof (pub)));
+}
+
+
+/**
* Send reserve status information to client.
*
* @param connection connection to the client
diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h
index c7139bf20..85c2e1f32 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -248,6 +248,44 @@ TMH_RESPONSE_reply_deposit_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
+TMH_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
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
+ unsigned int response_code);
+
+
+/**
+ * Generate successful refund confirmation message.
+ *
+ * @param connection connection to the client
+ * @param refund details about the successful refund
+ * @return MHD result code
+ */
+int
+TMH_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.
diff --git a/src/exchangedb/plugin_exchangedb_common.c b/src/exchangedb/plugin_exchangedb_common.c
index c8e689cfd..c18b958cf 100644
--- a/src/exchangedb/plugin_exchangedb_common.c
+++ b/src/exchangedb/plugin_exchangedb_common.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 GNUnet e.V.
+ Copyright (C) 2015, 2016 Inria & GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -107,6 +107,9 @@ common_free_coin_transaction_list (void *cls,
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
GNUNET_free (list->details.melt);
break;
+ case TALER_EXCHANGEDB_TT_REFUND:
+ GNUNET_free (list->details.refund);
+ break;
}
GNUNET_free (list);
list = next;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 612dabb79..b189b7a17 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2177,6 +2177,26 @@ postgres_mark_deposit_tiny (void *cls,
/**
+ * 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)
+ */
+static int
+postgres_test_deposit_done (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_EXCHANGEDB_Deposit *deposit)
+{
+ GNUNET_break (0); // not implemented
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Mark a deposit as done, thereby declaring that it cannot be
* executed at all anymore, and should no longer be returned by
* @e iterate_ready_deposits() or @e iterate_matching_deposits().
@@ -2467,6 +2487,24 @@ postgres_insert_deposit (void *cls,
/**
+ * Insert information about refunded coin into the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param refund refund information to store
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+postgres_insert_refund (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_EXCHANGEDB_Refund *refund)
+{
+ GNUNET_break (0); // not implemented
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Lookup refresh session data under the given @a session_hash.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -4242,10 +4280,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->free_reserve_history = &common_free_reserve_history;
plugin->have_deposit = &postgres_have_deposit;
plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny;
+ plugin->test_deposit_done = &postgres_test_deposit_done;
plugin->mark_deposit_done = &postgres_mark_deposit_done;
plugin->get_ready_deposit = &postgres_get_ready_deposit;
plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
plugin->insert_deposit = &postgres_insert_deposit;
+ plugin->insert_refund = &postgres_insert_refund;
plugin->get_refresh_session = &postgres_get_refresh_session;
plugin->create_refresh_session = &postgres_create_refresh_session;
plugin->insert_refresh_melt = &postgres_insert_refresh_melt;
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 8e00be751..7dcdb698f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -499,7 +499,12 @@ enum TALER_EXCHANGEDB_TransactionType
/**
* /refresh/melt operation.
*/
- TALER_EXCHANGEDB_TT_REFRESH_MELT = 1
+ TALER_EXCHANGEDB_TT_REFRESH_MELT = 1,
+
+ /**
+ * /refund operation.
+ */
+ TALER_EXCHANGEDB_TT_REFUND = 2
};
@@ -536,6 +541,11 @@ struct TALER_EXCHANGEDB_TransactionList
*/
struct TALER_EXCHANGEDB_RefreshMelt *melt;
+ /**
+ * Details if transaction was a /refund operation.
+ */
+ struct TALER_EXCHANGEDB_Refund *refund;
+
} details;
};
@@ -954,6 +964,20 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Insert information about refunded coin into the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param refund refund information to store
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+ int
+ (*insert_refund) (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_EXCHANGEDB_Refund *refund);
+
+
+ /**
* Mark a deposit as tiny, thereby declaring that it cannot be
* executed by itself and should no longer be returned by
* @e iterate_ready_deposits()
@@ -970,6 +994,22 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * 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)
+ */
+ int
+ (*test_deposit_done) (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_EXCHANGEDB_Deposit *deposit);
+
+
+ /**
* Mark a deposit as done, thereby declaring that it cannot be
* executed at all anymore, and should no longer be returned by
* @e iterate_ready_deposits() or @e iterate_matching_deposits().