From e781a77a66d292ba28b4e7a11b460f07daca9232 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 7 Apr 2020 18:47:45 +0200 Subject: fix #6129 (/refund idempotency) --- src/backend/taler-merchant-httpd_refund_increase.c | 242 ++++++++++----------- src/backend/taler-merchant-httpd_refund_lookup.c | 6 - src/backenddb/plugin_merchantdb_postgres.c | 56 ++--- src/backenddb/test_merchantdb.c | 15 +- src/include/taler_merchantdb_plugin.h | 232 ++++++++++---------- 5 files changed, 256 insertions(+), 295 deletions(-) diff --git a/src/backend/taler-merchant-httpd_refund_increase.c b/src/backend/taler-merchant-httpd_refund_increase.c index 33cf4d99..99a5bcbc 100644 --- a/src/backend/taler-merchant-httpd_refund_increase.c +++ b/src/backend/taler-merchant-httpd_refund_increase.c @@ -129,82 +129,26 @@ json_parse_cleanup (struct TM_HandlerContext *hc) /** - * Handle request for increasing the refund associated with - * a contract. + * Process a refund request. * - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL + * @param connection HTTP client connection + * @param mi merchant instance doing the processing + * @param refund amount to be refunded + * @param order_id for which order is the refund + * @param reason reason for the refund * @return MHD result code */ -int -MH_handler_refund_increase (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) +static int +process_refund (struct MHD_Connection *connection, + struct MerchantInstance *mi, + const struct TALER_Amount *refund, + const char *order_id, + const char *reason) { - int res; - struct TMH_JsonParseContext *ctx; - struct TALER_Amount refund; - json_t *root; json_t *contract_terms; - const char *order_id; - const char *reason; - struct GNUNET_HashCode h_contract_terms; - struct GNUNET_CRYPTO_EddsaSignature sig; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("refund", &refund), - GNUNET_JSON_spec_string ("order_id", &order_id), - GNUNET_JSON_spec_string ("reason", &reason), - GNUNET_JSON_spec_end () - }; enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qsx; - - if (NULL == *connection_cls) - { - ctx = GNUNET_new (struct TMH_JsonParseContext); - ctx->hc.cc = &json_parse_cleanup; - *connection_cls = ctx; - } - else - { - ctx = *connection_cls; - } - res = TALER_MHD_parse_post_json (connection, - &ctx->json_parse_context, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - return MHD_NO; - /* the POST's body has to be further fetched */ - if ( (GNUNET_NO == res) || - (NULL == root) ) - return MHD_YES; - - res = TALER_MHD_parse_json_data (connection, - root, - spec); - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - json_decref (root); - return MHD_YES; - } - if (GNUNET_SYSERR == res) - { - GNUNET_break_op (0); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_JSON_INVALID, - "Request body does not match specification"); - } + struct GNUNET_HashCode h_contract_terms; db->preflight (db->cls); /* Convert order id to h_contract_terms */ @@ -219,7 +163,6 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); /* Always report on hard error as well to enable diagnostics */ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - json_decref (root); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_REFUND_LOOKUP_DB_ERROR, @@ -230,7 +173,6 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Unknown order id given: `%s'\n", order_id); - json_decref (root); return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_REFUND_ORDER_ID_UNKNOWN, @@ -242,14 +184,13 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, &h_contract_terms)) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); json_decref (contract_terms); - json_decref (root); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_INTERNAL_LOGIC_ERROR, "Could not hash contract terms"); } + json_decref (contract_terms); for (unsigned int i = 0; iincrease_refund_for_contract_NT (db->cls, &h_contract_terms, &mi->pubkey, - &refund, + refund, reason); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "increase refund returned %d\n", @@ -302,9 +241,6 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); /* Always report on hard error as well to enable diagnostics */ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - json_decref (root); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_REFUND_MERCHANT_DB_COMMIT_ERROR, @@ -312,53 +248,13 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Refunded amount lower or equal to previous refund: %s\n", - TALER_amount2s (&refund)); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - json_decref (root); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Refusing refund amount %s that is larger than original payment\n", + TALER_amount2s (refund)); return TALER_MHD_reply_with_error (connection, MHD_HTTP_CONFLICT, TALER_EC_REFUND_INCONSISTENT_AMOUNT, - "Amount incorrect: not larger than the previous one"); - } - - /** - * Return to the frontend at this point. The frontend will then return - * a "402 Payment required" carrying a "X-Taler-Refund-Url: www" - * where 'www' is the URL where the wallet can automatically fetch - * the refund permission. - * - * Just a "200 OK" should be fine here, as the frontend has all - * the information needed to generate the right response. - */// - { - struct TALER_MerchantRefundConfirmationPS confirmation = { - .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK), - .purpose.size = htonl (sizeof (confirmation)) - }; - - GNUNET_CRYPTO_hash (order_id, - strlen (order_id), - &confirmation.h_order_id); - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, - &confirmation.purpose, - &sig)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to sign successful refund confirmation\n"); - json_decref (contract_terms); - GNUNET_JSON_parse_free (spec); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_MERCHANT_SIGNING_FAILED, - "Refund done, but failed to sign confirmation"); - - } + "Amount above payment"); } { @@ -368,18 +264,102 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, taler_refund_uri = make_taler_refund_uri (connection, mi->id, order_id); - ret = TALER_MHD_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:o, s:o, s:s}", - "sig", GNUNET_JSON_from_data_auto (&sig), - "contract_terms", contract_terms, - "taler_refund_uri", taler_refund_uri); + ret = TALER_MHD_reply_json_pack ( + connection, + MHD_HTTP_OK, + "{s:o, s:s}", + "h_contract_terms", + GNUNET_JSON_from_data_auto (&h_contract_terms), + "taler_refund_url", + taler_refund_uri); GNUNET_free (taler_refund_uri); - GNUNET_JSON_parse_free (spec); - json_decref (root); return ret; } } +/** + * Handle request for increasing the refund associated with + * a contract. + * + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param mi merchant backend instance, never NULL + * @return MHD result code + */ +int +MH_handler_refund_increase (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size, + struct MerchantInstance *mi) +{ + int res; + struct TMH_JsonParseContext *ctx; + struct TALER_Amount refund; + const char *order_id; + const char *reason; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount ("refund", &refund), + GNUNET_JSON_spec_string ("order_id", &order_id), + GNUNET_JSON_spec_string ("reason", &reason), + GNUNET_JSON_spec_end () + }; + json_t *root; + + if (NULL == *connection_cls) + { + ctx = GNUNET_new (struct TMH_JsonParseContext); + ctx->hc.cc = &json_parse_cleanup; + *connection_cls = ctx; + } + else + { + ctx = *connection_cls; + } + + res = TALER_MHD_parse_post_json (connection, + &ctx->json_parse_context, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + return MHD_NO; + /* the POST's body has to be further fetched */ + if ( (GNUNET_NO == res) || + (NULL == root) ) + return MHD_YES; + + res = TALER_MHD_parse_json_data (connection, + root, + spec); + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + json_decref (root); + return MHD_YES; + } + if (GNUNET_SYSERR == res) + { + GNUNET_break_op (0); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_JSON_INVALID, + "Request body does not match specification"); + } + res = process_refund (connection, + mi, + &refund, + order_id, + reason); + GNUNET_JSON_parse_free (spec); + json_decref (root); + return res; +} + + /* end of taler-merchant-httpd_refund_increase.c */ diff --git a/src/backend/taler-merchant-httpd_refund_lookup.c b/src/backend/taler-merchant-httpd_refund_lookup.c index 36a6b88f..d54a87c3 100644 --- a/src/backend/taler-merchant-httpd_refund_lookup.c +++ b/src/backend/taler-merchant-httpd_refund_lookup.c @@ -25,12 +25,6 @@ #include "taler-merchant-httpd.h" #include "taler-merchant-httpd_refund.h" -/** - * How often do we retry the non-trivial refund INSERT database - * transaction? - */ -#define MAX_RETRIES 5 - /** * Return refund situation about a contract. diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 0f547e3d..7ee776ba 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -2054,7 +2054,7 @@ process_refund_cb (void *cls, /** - * Closure for #process_deposits_cb. + * Closure for #process_deposits_for_refund_cb. */ struct InsertRefundContext { @@ -2198,7 +2198,9 @@ process_deposits_for_refund_cb (void *cls, if (0 >= TALER_amount_cmp (ctx->refund, ¤t_refund)) { - ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Existing refund of %s at or above requested refund. Finished early.\n", + TALER_amount2s (¤t_refund)); return; } @@ -2301,11 +2303,11 @@ process_deposits_for_refund_cb (void *cls, * Although this should be checked as the business should never * issue a refund bigger than the contract's actual price, we cannot * rely upon the frontend being correct. - */GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + */// + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "The refund of %s is bigger than the order's value\n", TALER_amount2s (ctx->refund)); - - ctx->qs = GNUNET_DB_STATUS_HARD_ERROR; + ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } @@ -2322,39 +2324,40 @@ process_deposits_for_refund_cb (void *cls, * @param reason 0-terminated UTF-8 string giving the reason why the customer * got a refund (free form, business-specific) * @return transaction status - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the refund is accepted - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the refund cannot be issued: this can happen for two - * reasons: the issued refund is not greater of the previous refund, - * or the coins don't have enough amount left to pay for this refund. + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we + * were originally paid and thus the transaction failed; + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid, + * regardless of whether it actually increased the refund beyond + * what was already refunded (idempotency!) */ static enum GNUNET_DB_QueryStatus -postgres_increase_refund_for_contract_NT (void *cls, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_Amount *refund, - const char *reason) +postgres_increase_refund_for_contract_NT ( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_Amount *refund, + const char *reason) { struct PostgresClosure *pg = cls; - struct InsertRefundContext ctx; enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; + struct InsertRefundContext ctx = { + .pg = pg, + .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + .refund = refund, + .reason = reason, + .h_contract_terms = h_contract_terms, + .merchant_pub = merchant_pub + }; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to refund %s on contract %s\n", TALER_amount2s (refund), GNUNET_h2s (h_contract_terms)); - ctx.pg = pg; - ctx.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - ctx.refund = refund; - ctx.reason = reason; - ctx.h_contract_terms = h_contract_terms; - ctx.merchant_pub = merchant_pub; qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "find_deposits", params, @@ -2363,11 +2366,8 @@ postgres_increase_refund_for_contract_NT (void *cls, switch (qs) { case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown contract: %s (merchant_pub: %s), no refund possible\n", - GNUNET_h2s (h_contract_terms), - TALER_B2S (merchant_pub)); - return qs; + /* never paid, means we clearly cannot refund anything */ + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; case GNUNET_DB_STATUS_SOFT_ERROR: case GNUNET_DB_STATUS_HARD_ERROR: return qs; diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index ff1e2b0a..87b8c734 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -981,21 +981,19 @@ run (void *cls) &refund_amount, "refund testing")); - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->increase_refund_for_contract_NT (plugin->cls, &h_contract_terms, &merchant_pub, &refund_amount, "same refund amount as " - "the previous one, should succeed without changes (0)")); - - /*Should fail as this refund a lesser amount respect to the previous one*/ - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + "the previous one, should succeed without changes (1)")); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->increase_refund_for_contract_NT (plugin->cls, &h_contract_terms, &merchant_pub, &little_refund_amount, - "lower refund amount as the previous one, should succeed without changes (0)")); + "lower refund amount as the previous one, should succeed without changes (1)")); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->increase_refund_for_contract_NT (plugin->cls, &h_contract_terms, @@ -1003,13 +1001,12 @@ run (void *cls) &right_second_refund_amount, "right refund increase")); - FAILIF (GNUNET_DB_STATUS_HARD_ERROR != + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->increase_refund_for_contract_NT (plugin->cls, &h_contract_terms, &merchant_pub, &too_big_refund_amount, - "make refund testing fail due" - " to too big refund amount")); + "make refund testing fail due to too big refund amount")); FAILIF (GNUNET_OK != test_wire_fee ()); diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 38c18863..cc2e6bd9 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017 INRIA + Copyright (C) 2014-2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -58,16 +58,14 @@ typedef void * @param total_amount total amount we receive for the contract after fees */ typedef void -(*TALER_MERCHANTDB_TransactionCallback)(void *cls, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - const struct - GNUNET_HashCode *h_contract_terms, - const struct GNUNET_HashCode *h_wire, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute refund, - const struct - TALER_Amount *total_amount); +(*TALER_MERCHANTDB_TransactionCallback)( + void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_contract_terms, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund, + const struct TALER_Amount *total_amount); /** @@ -85,18 +83,16 @@ typedef void * matches the `interface DepositSuccess` of the documentation. */ typedef void -(*TALER_MERCHANTDB_CoinDepositCallback)(void *cls, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_CoinSpendPublicKeyP *coin_pub, - const char *exchange_url, - const struct - TALER_Amount *amount_with_fee, - const struct TALER_Amount *deposit_fee, - const struct TALER_Amount *refund_fee, - const struct TALER_Amount *wire_fee, - const json_t *exchange_proof); +(*TALER_MERCHANTDB_CoinDepositCallback)( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const char *exchange_url, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + const struct TALER_Amount *refund_fee, + const struct TALER_Amount *wire_fee, + const json_t *exchange_proof); /** @@ -117,15 +113,13 @@ typedef void * NULL if we have not asked for this signature */ typedef void -(*TALER_MERCHANTDB_TransferCallback)(void *cls, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_CoinSpendPublicKeyP *coin_pub, - const struct - TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute execution_time, - const json_t *exchange_proof); +(*TALER_MERCHANTDB_TransferCallback)( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute execution_time, + const json_t *exchange_proof); /** @@ -150,13 +144,13 @@ typedef void * @param refund_fee cost of this refund operation */ typedef void -(*TALER_MERCHANTDB_RefundCallback)(void *cls, - const struct - TALER_CoinSpendPublicKeyP *coin_pub, - uint64_t rtransaction_id, - const char *reason, - const struct TALER_Amount *refund_amount, - const struct TALER_Amount *refund_fee); +(*TALER_MERCHANTDB_RefundCallback)( + void *cls, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t rtransaction_id, + const char *reason, + const struct TALER_Amount *refund_amount, + const struct TALER_Amount *refund_fee); /** @@ -325,12 +319,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_contract_terms_from_hash)(void *cls, - json_t **contract_terms, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub); + (*find_contract_terms_from_hash)( + void *cls, + json_t **contract_terms, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub); /** @@ -343,12 +336,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_paid_contract_terms_from_hash)(void *cls, - json_t **contract_terms, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub); + (*find_paid_contract_terms_from_hash)( + void *cls, + json_t **contract_terms, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub); /** @@ -371,18 +363,16 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_contract_terms_by_date_and_range)(void *cls, - struct GNUNET_TIME_Absolute date, - const struct - TALER_MerchantPublicKeyP * - merchant_pub, - uint64_t start, - uint64_t nrows, - int past, - unsigned int ascending, - TALER_MERCHANTDB_ProposalDataCallback - cb, - void *cb_cls); + (*find_contract_terms_by_date_and_range)( + void *cls, + struct GNUNET_TIME_Absolute date, + const struct TALER_MerchantPublicKeyP *merchant_pub, + uint64_t start, + uint64_t nrows, + int past, + unsigned int ascending, + TALER_MERCHANTDB_ProposalDataCallback cb, + void *cb_cls); /** * Lookup for a proposal, respecting the signature used by the @@ -396,12 +386,12 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_contract_terms_history)(void *cls, - const char *order_id, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - TALER_MERCHANTDB_ProposalDataCallback cb, - void *cb_cls); + (*find_contract_terms_history)( + void *cls, + const char *order_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + TALER_MERCHANTDB_ProposalDataCallback cb, + void *cb_cls); /** @@ -418,13 +408,13 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_contract_terms_by_date)(void *cls, - struct GNUNET_TIME_Absolute date, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - uint64_t nrows, - TALER_MERCHANTDB_ProposalDataCallback cb, - void *cb_cls); + (*find_contract_terms_by_date)( + void *cls, + struct GNUNET_TIME_Absolute date, + const struct TALER_MerchantPublicKeyP *merchant_pub, + uint64_t nrows, + TALER_MERCHANTDB_ProposalDataCallback cb, + void *cb_cls); /** @@ -468,11 +458,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*store_coin_to_transfer)(void *cls, - const struct GNUNET_HashCode *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct - TALER_WireTransferIdentifierRawP *wtid); + (*store_coin_to_transfer)( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_WireTransferIdentifierRawP *wtid); /** @@ -487,13 +477,13 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*store_transfer_to_proof)(void *cls, - const char *exchange_url, - const struct - TALER_WireTransferIdentifierRawP *wtid, - struct GNUNET_TIME_Absolute execution_time, - const struct TALER_ExchangePublicKeyP *signkey_pub, - const json_t *exchange_proof); + (*store_transfer_to_proof)( + void *cls, + const char *exchange_url, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct GNUNET_TIME_Absolute execution_time, + const struct TALER_ExchangePublicKeyP *signkey_pub, + const json_t *exchange_proof); /** @@ -512,16 +502,15 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*store_wire_fee_by_exchange)(void *cls, - const struct - TALER_MasterPublicKeyP *exchange_pub, - const struct GNUNET_HashCode *h_wire_method, - const struct TALER_Amount *wire_fee, - const struct TALER_Amount *closing_fee, - struct GNUNET_TIME_Absolute start_date, - struct GNUNET_TIME_Absolute end_date, - const struct - TALER_MasterSignatureP *exchange_sig); + (*store_wire_fee_by_exchange)( + void *cls, + const struct TALER_MasterPublicKeyP *exchange_pub, + const struct GNUNET_HashCode *h_wire_method, + const struct TALER_Amount *wire_fee, + const struct TALER_Amount *closing_fee, + struct GNUNET_TIME_Absolute start_date, + struct GNUNET_TIME_Absolute end_date, + const struct TALER_MasterSignatureP *exchange_sig); /** @@ -556,15 +545,13 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*find_payments_by_hash_and_coin)(void *cls, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - const struct - TALER_CoinSpendPublicKeyP *coin_pub, - TALER_MERCHANTDB_CoinDepositCallback cb, - void *cb_cls); + (*find_payments_by_hash_and_coin)( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + TALER_MERCHANTDB_CoinDepositCallback cb, + void *cb_cls); /** @@ -663,15 +650,19 @@ struct TALER_MERCHANTDB_Plugin * @param reason 0-terminated UTF-8 string giving the reason why the customer * got a refund (free form, business-specific) * @return transaction status + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we + * were originally paid and thus the transaction failed; + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid, + * regardless of whether it actually increased the refund beyond + * what was already refunded (idempotency!) */ enum GNUNET_DB_QueryStatus - (*increase_refund_for_contract_NT)(void *cls, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_Amount *refund, - const char *reason); + (*increase_refund_for_contract_NT)( + void *cls, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_Amount *refund, + const char *reason); /** @@ -685,13 +676,12 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*get_refunds_from_contract_terms_hash)(void *cls, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - const struct - GNUNET_HashCode *h_contract_terms, - TALER_MERCHANTDB_RefundCallback rc, - void *rc_cls); + (*get_refunds_from_contract_terms_hash)( + void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_contract_terms, + TALER_MERCHANTDB_RefundCallback rc, + void *rc_cls); /** * Add @a credit to a reserve to be used for tipping. Note that -- cgit v1.2.3