summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_refund.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_refund.c')
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c509
1 files changed, 147 insertions, 362 deletions
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index 9fd9575f9..b8bcf7c60 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -32,7 +32,7 @@
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_refund.h"
#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
/**
@@ -43,46 +43,63 @@
* @param refund details about the successful refund
* @return MHD result code
*/
-static int
+static MHD_RESULT
reply_refund_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_EXCHANGEDB_RefundListEntry *refund)
{
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
- struct TALER_RefundConfirmationPS rc = {
- .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND),
- .purpose.size = htonl (sizeof (rc)),
- .h_contract_terms = refund->h_contract_terms,
- .coin_pub = *coin_pub,
- .merchant = refund->merchant_pub,
- .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);
- if (GNUNET_OK !=
- TEH_KS_sign (&rc.purpose,
- &pub,
- &sig))
+ enum TALER_ErrorCode ec;
+
+ if (TALER_EC_NONE !=
+ (ec = TALER_exchange_online_refund_confirmation_sign (
+ &TEH_keys_exchange_sign_,
+ &refund->h_contract_terms,
+ coin_pub,
+ &refund->merchant_pub,
+ refund->rtransaction_id,
+ &refund->refund_amount,
+ &pub,
+ &sig)))
{
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_BAD_CONFIGURATION,
- "no online signing key");
+ return TALER_MHD_reply_with_ec (connection,
+ ec,
+ NULL);
}
- return TALER_MHD_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));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_data_auto ("exchange_sig",
+ &sig),
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &pub));
}
/**
+ * Closure for refund_transaction().
+ */
+struct RefundContext
+{
+ /**
+ * Details about the deposit operation.
+ */
+ const struct TALER_EXCHANGEDB_Refund *refund;
+
+ /**
+ * Deposit fee of the coin.
+ */
+ struct TALER_Amount deposit_fee;
+
+ /**
+ * Unique ID of the coin in known_coins.
+ */
+ uint64_t known_coin_id;
+};
+
+
+/**
* 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.
@@ -95,7 +112,6 @@ reply_refund_success (struct MHD_Connection *connection,
*
* @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
@@ -103,231 +119,70 @@ reply_refund_success (struct MHD_Connection *connection,
static enum GNUNET_DB_QueryStatus
refund_transaction (void *cls,
struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- int *mhd_ret)
+ MHD_RESULT *mhd_ret)
{
- const struct TALER_EXCHANGEDB_Refund *refund = cls;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- const struct TALER_EXCHANGEDB_DepositListEntry *dep;
- const struct TALER_EXCHANGEDB_RefundListEntry *ref;
+ struct RefundContext *rctx = cls;
+ const struct TALER_EXCHANGEDB_Refund *refund = rctx->refund;
enum GNUNET_DB_QueryStatus qs;
- int deposit_found;
- int refund_found;
+ bool not_found;
+ bool refund_ok;
+ bool conflict;
+ bool gone;
- tl = NULL;
- qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &refund->coin.coin_pub,
- GNUNET_NO,
- &tl);
+ /* Finally, store new refund data */
+ qs = TEH_plugin->do_refund (TEH_plugin->cls,
+ refund,
+ &rctx->deposit_fee,
+ rctx->known_coin_id,
+ &not_found,
+ &refund_ok,
+ &gone,
+ &conflict);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
*mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND,
- "database transaction failure");
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "do refund");
return qs;
}
- dep = NULL;
- ref = NULL;
- 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->details.merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.deposit->h_contract_terms,
- &refund->details.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) )
- {
- dep = tlp->details.deposit;
- deposit_found = GNUNET_YES;
- break;
- }
- }
- break;
- case TALER_EXCHANGEDB_TT_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->details.merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.refund->h_contract_terms,
- &refund->details.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) &&
- (tlp->details.refund->rtransaction_id ==
- refund->details.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->details.merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.refund->h_contract_terms,
- &refund->details.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) &&
- (tlp->details.refund->rtransaction_id !=
- refund->details.rtransaction_id) )
- {
- 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_OLD_COIN_RECOUP:
- /* Recoups cannot be refunded, ignore here */
- break;
- case TALER_EXCHANGEDB_TT_RECOUP:
- /* Recoups cannot be refunded, ignore here */
- break;
- case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
- /* Recoups cannot be refunded, ignore here */
- break;
- }
- }
- /* handle if deposit was NOT found */
- if (GNUNET_NO == deposit_found)
+
+ if (gone)
{
- TALER_LOG_WARNING ("Deposit to /refund was not found\n");
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
*mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_DEPOSIT_NOT_FOUND,
- "deposit unknown");
+ MHD_HTTP_GONE,
+ TALER_EC_EXCHANGE_REFUND_MERCHANT_ALREADY_PAID,
+ NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* handle if conflicting refund found */
- if (GNUNET_SYSERR == refund_found)
+ if (conflict)
{
- *mhd_ret = TALER_MHD_reply_json_pack (
+ GNUNET_break_op (0);
+ *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
- MHD_HTTP_CONFLICT,
- "{s:s, s:I, s:o}",
- "hint", "conflicting refund",
- "code", (json_int_t) TALER_EC_REFUND_CONFLICT,
- "history", TEH_RESPONSE_compile_transaction_history (
- &refund->coin.coin_pub,
- tl));
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
+ TALER_EC_EXCHANGE_REFUND_INCONSISTENT_AMOUNT,
+ &refund->coin.denom_pub_hash,
+ &refund->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* handle if identical refund found */
- if (GNUNET_YES == refund_found)
+ if (not_found)
{
- /* /refund already done, simply re-transmit confirmation */
- *mhd_ret = reply_refund_success (connection,
- &refund->coin.coin_pub,
- 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->details.refund_amount,
- &dep->amount_with_fee)) ||
- (GNUNET_YES !=
- TALER_amount_cmp_currency (&refund->details.refund_fee,
- &dep->deposit_fee)) )
- {
- GNUNET_break_op (0); /* currency mismatch */
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_CURRENCY_MISMATCH,
- "currencies involved do not match");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
- /* check if we already send the money for the /deposit */
- qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
- session,
- &refund->coin.coin_pub,
- &dep->merchant_pub,
- &dep->h_contract_terms,
- &dep->h_wire);
- 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 = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFUND_DB_INCONSISTENT,
- "database inconsistent (deposit data became inaccessible during transaction)");
- 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 = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_GONE,
- TALER_EC_REFUND_MERCHANT_ALREADY_PAID,
- "money already sent to merchant");
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_REFUND_DEPOSIT_NOT_FOUND,
+ NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
-
- /* check refund amount is sufficiently low */
- if (1 == TALER_amount_cmp (&refund->details.refund_amount,
- &dep->amount_with_fee) )
+ if (! refund_ok)
{
- GNUNET_break_op (0); /* cannot refund more than original value */
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_INSUFFICIENT_FUNDS,
- "refund requested exceeds original value");
+ *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
+ connection,
+ TALER_EC_EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT,
+ &refund->coin.denom_pub_hash,
+ &refund->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- 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 = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFUND_STORE_DB_ERROR,
- "could not persist store information");
- return qs;
- }
- /* Success or soft failure */
return qs;
}
@@ -339,42 +194,31 @@ refund_transaction (void *cls,
* the fee structure, so this is not done here.
*
* @param connection the MHD connection to handle
- * @param refund information about the refund
+ * @param[in,out] refund information about the refund
* @return MHD result code
*/
-static int
+static MHD_RESULT
verify_and_execute_refund (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Refund *refund)
+ struct TALER_EXCHANGEDB_Refund *refund)
{
- struct GNUNET_HashCode denom_hash;
- struct TALER_Amount expect_fee;
+ struct RefundContext rctx = {
+ .refund = refund
+ };
+ TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
+ if (GNUNET_OK !=
+ TALER_merchant_refund_verify (&refund->coin.coin_pub,
+ &refund->details.h_contract_terms,
+ refund->details.rtransaction_id,
+ &refund->details.refund_amount,
+ &refund->details.merchant_pub,
+ &refund->details.merchant_sig))
{
- struct TALER_RefundRequestPS rr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
- .purpose.size = htonl (sizeof (rr)),
- .h_contract_terms = refund->details.h_contract_terms,
- .coin_pub = refund->coin.coin_pub,
- .merchant = refund->details.merchant_pub,
- .rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id)
- };
-
- TALER_amount_hton (&rr.refund_amount,
- &refund->details.refund_amount);
- TALER_amount_hton (&rr.refund_fee,
- &refund->details.refund_fee);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
- &rr.purpose,
- &refund->details.merchant_sig.eddsa_sig,
- &refund->details.merchant_pub.eddsa_pub))
- {
- TALER_LOG_WARNING ("Invalid signature on refund request\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
- "merchant_sig");
- }
+ TALER_LOG_WARNING ("Invalid signature on refund request\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_REFUND_MERCHANT_SIGNATURE_INVALID,
+ NULL);
}
/* Fetch the coin's denomination (hash) */
@@ -382,100 +226,57 @@ verify_and_execute_refund (struct MHD_Connection *connection,
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_coin_denomination (TEH_plugin->cls,
- NULL,
&refund->coin.coin_pub,
- &denom_hash);
+ &rctx.known_coin_id,
+ &refund->coin.denom_pub_hash);
if (0 > qs)
{
+ MHD_RESULT res;
+ char *dhs;
+
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND,
- "denomination of coin to be refunded not found in DB");
+ dhs = GNUNET_STRINGS_data_to_string_alloc (
+ &refund->coin.denom_pub_hash,
+ sizeof (refund->coin.denom_pub_hash));
+ res = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_REFUND_COIN_NOT_FOUND,
+ dhs);
+ GNUNET_free (dhs);
+ return res;
}
}
{
- struct TEH_KS_StateHandle *key_state;
-
- key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
- if (NULL == key_state)
- {
- TALER_LOG_ERROR ("Lacking keys to operate\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_BAD_CONFIGURATION,
- "no keys");
- }
/* Obtain information about the coin's denomination! */
- {
- struct TALER_EXCHANGEDB_DenominationKey *dki;
- unsigned int hc;
- enum TALER_ErrorCode ec;
-
- dki = TEH_KS_denomination_key_lookup_by_hash (key_state,
- &denom_hash,
- TEH_KS_DKU_DEPOSIT,
- &ec,
- &hc);
- 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 (key_state);
- return TALER_MHD_reply_with_error (connection,
- hc,
- ec,
- "denomination not found, but coin known");
- }
- TALER_amount_ntoh (&expect_fee,
- &dki->issue.properties.fee_refund);
- }
- TEH_KS_release (key_state);
- }
+ struct TEH_DenominationKey *dk;
+ MHD_RESULT mret;
- /* Check refund fee matches fee of denomination key! */
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&expect_fee,
- &refund->details.refund_fee) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_CURRENCY_MISMATCH,
- "refund_fee");
- }
- {
- int fee_cmp;
-
- fee_cmp = TALER_amount_cmp (&refund->details.refund_fee,
- &expect_fee);
- if (-1 == fee_cmp)
- {
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_TOO_LOW,
- "refund_fee");
- }
- if (1 == fee_cmp)
+ dk = TEH_keys_denomination_by_hash (&refund->coin.denom_pub_hash,
+ connection,
+ &mret);
+ if (NULL == dk)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Refund fee proposed by merchant is higher than necessary.\n");
+ /* DKI not found, but we do have a coin with this DK in our database;
+ not good... */
+ GNUNET_break (0);
+ return mret;
}
+ refund->details.refund_fee = dk->meta.fees.refund;
+ rctx.deposit_fee = dk->meta.fees.deposit;
}
-
/* Finally run the actual transaction logic */
{
- int mhd_ret;
+ MHD_RESULT mhd_ret;
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
"run refund",
+ TEH_MT_REQUEST_OTHER,
&mhd_ret,
&refund_transaction,
- (void *) refund))
+ &rctx))
{
return mhd_ret;
}
@@ -497,27 +298,32 @@ verify_and_execute_refund (struct MHD_Connection *connection,
* @param root uploaded JSON data
* @return MHD result code
*/
-int
+MHD_RESULT
TEH_handler_refund (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const json_t *root)
{
- struct TALER_EXCHANGEDB_Refund refund;
+ struct TALER_EXCHANGEDB_Refund refund = {
+ .details.refund_fee.currency = {0} /* set to invalid, just to be sure */
+ };
struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount ("refund_amount", &refund.details.refund_amount),
- TALER_JSON_spec_amount ("refund_fee", &refund.details.refund_fee),
+ TALER_JSON_spec_amount ("refund_amount",
+ TEH_currency,
+ &refund.details.refund_amount),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&refund.details.h_contract_terms),
- GNUNET_JSON_spec_fixed_auto ("merchant_pub", &refund.details.merchant_pub),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+ &refund.details.merchant_pub),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&refund.details.rtransaction_id),
- GNUNET_JSON_spec_fixed_auto ("merchant_sig", &refund.details.merchant_sig),
+ GNUNET_JSON_spec_fixed_auto ("merchant_sig",
+ &refund.details.merchant_sig),
GNUNET_JSON_spec_end ()
};
refund.coin.coin_pub = *coin_pub;
{
- int res;
+ enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection,
root,
@@ -527,29 +333,8 @@ TEH_handler_refund (struct MHD_Connection *connection,
if (GNUNET_NO == res)
return MHD_YES; /* failure */
}
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&refund.details.refund_amount,
- &refund.details.refund_fee) )
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_CURRENCY_MISMATCH,
- "refund_amount or refund_fee");
- }
- if (-1 == TALER_amount_cmp (&refund.details.refund_amount,
- &refund.details.refund_fee) )
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
- "refund_amount");
- }
{
- int res;
+ MHD_RESULT res;
res = verify_and_execute_refund (connection,
&refund);