merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 3a38124fa780be01a06bb7d3a3c7d51824f256cf
parent 65950734eebc72a431724af163b51f7a04640ab9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu,  4 Apr 2019 23:14:52 +0200

clean up code duplication in checking of tipping reserve status by moving shared logic to helper function

Diffstat:
Msrc/backend/Makefile.am | 1+
Msrc/backend/taler-merchant-httpd_responses.c | 44++++++++++++++++++++++----------------------
Msrc/backend/taler-merchant-httpd_tip-authorize.c | 226++++++++++---------------------------------------------------------------------
Msrc/backend/taler-merchant-httpd_tip-query.c | 418++++++++++++-------------------------------------------------------------------
Asrc/backend/taler-merchant-httpd_tip-reserve-helper.c | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_tip-reserve-helper.h | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/test_merchant_api_new.c | 4++--
7 files changed, 582 insertions(+), 577 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -25,6 +25,7 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \ taler-merchant-httpd_tip-pickup.c taler-merchant-httpd_tip-pickup.h \ taler-merchant-httpd_tip-query.c taler-merchant-httpd_tip-query.h \ + taler-merchant-httpd_tip-reserve-helper.c taler-merchant-httpd_tip-reserve-helper.h \ taler-merchant-httpd_track-transaction.c taler-merchant-httpd_track-transaction.h \ taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h \ taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \ diff --git a/src/backend/taler-merchant-httpd_responses.c b/src/backend/taler-merchant-httpd_responses.c @@ -43,14 +43,14 @@ TMH_RESPONSE_make_json (const json_t *json) char *json_str; json_str = json_dumps (json, - JSON_INDENT(2)); + JSON_INDENT(2)); if (NULL == json_str) { GNUNET_break (0); return NULL; } resp = MHD_create_response_from_buffer (strlen (json_str), - json_str, + json_str, MHD_RESPMEM_MUST_FREE); if (NULL == resp) { @@ -152,9 +152,9 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection, va_start (argp, fmt); json = json_vpack_ex (&jerror, - 0, - fmt, - argp); + 0, + fmt, + argp); va_end (argp); if (NULL == json) { @@ -182,10 +182,10 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection, */ struct MHD_Response * TMH_RESPONSE_make_error (enum TALER_ErrorCode ec, - const char *hint) + const char *hint) { return TMH_RESPONSE_make_json_pack ("{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "hint", hint); } @@ -200,13 +200,13 @@ TMH_RESPONSE_make_error (enum TALER_ErrorCode ec, */ int TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, + enum TALER_ErrorCode ec, const char *hint) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "hint", hint); } @@ -255,7 +255,7 @@ TMH_RESPONSE_reply_rc (struct MHD_Connection *connection, return TMH_RESPONSE_reply_json_pack (connection, response_code, "{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "error", msg); } @@ -272,7 +272,7 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection) return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:I, s:s}", - "code", (json_int_t) TALER_EC_JSON_INVALID, + "code", (json_int_t) TALER_EC_JSON_INVALID, "error", "invalid json"); } @@ -288,13 +288,13 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection) */ int TMH_RESPONSE_reply_not_found (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, + enum TALER_ErrorCode ec, const char *object) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_NOT_FOUND, "{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "error", object); } @@ -309,13 +309,13 @@ TMH_RESPONSE_reply_not_found (struct MHD_Connection *connection, */ int TMH_RESPONSE_reply_bad_request (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, + enum TALER_ErrorCode ec, const char *issue) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "error", issue); } @@ -347,13 +347,13 @@ TMH_RESPONSE_add_global_headers (struct MHD_Response *response) */ int TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, + enum TALER_ErrorCode ec, const char *hint) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:I, s:s}", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "hint", hint); } @@ -368,14 +368,14 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection, */ int TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, + enum TALER_ErrorCode ec, const char *param_name) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:s, s:I, s:s}", "error", "missing parameter", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "parameter", param_name); } @@ -390,14 +390,14 @@ TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection, */ int TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection, - enum TALER_ErrorCode ec, - const char *param_name) + enum TALER_ErrorCode ec, + const char *param_name) { return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:s, s:I, s:s}", "error", "invalid parameter", - "code", (json_int_t) ec, + "code", (json_int_t) ec, "parameter", param_name); } diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c b/src/backend/taler-merchant-httpd_tip-authorize.c @@ -28,6 +28,7 @@ #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_responses.h" #include "taler-merchant-httpd_tip-authorize.h" +#include "taler-merchant-httpd_tip-reserve-helper.h" struct TipAuthContext @@ -44,11 +45,6 @@ struct TipAuthContext void *json_parse_context; /** - * HTTP connection we are handling. - */ - struct MHD_Connection *connection; - - /** * Merchant instance to use. */ const char *instance; @@ -74,20 +70,9 @@ struct TipAuthContext json_t *root; /** - * Handle to pending /reserve/status request. - */ - struct TALER_EXCHANGE_ReserveStatusHandle *rsh; - - /** - * Handle for operation to obtain exchange handle. - */ - struct TMH_EXCHANGES_FindOperation *fo; - - /** - * Reserve expiration time as provided by the exchange. - * Set in #exchange_cont. + * Context for checking the tipping reserve's status. */ - struct GNUNET_TIME_Relative idle_reserve_expiration_time; + struct CheckTipReserve ctr; /** * Tip amount requested. @@ -95,11 +80,6 @@ struct TipAuthContext struct TALER_Amount amount; /** - * Private key used by this merchant for the tipping reserve. - */ - struct TALER_ReservePrivateKeyP reserve_priv; - - /** * Flag set to #GNUNET_YES when we have tried /reserve/status of the * tipping reserve already. */ @@ -110,10 +90,6 @@ struct TipAuthContext */ int parsed_json; - /** - * Error code witnessing what the Exchange complained about. - */ - enum TALER_ErrorCode exchange_ec; }; @@ -132,152 +108,13 @@ cleanup_tac (struct TM_HandlerContext *hc) json_decref (tac->root); tac->root = NULL; } - if (NULL != tac->rsh) - { - TALER_EXCHANGE_reserve_status_cancel (tac->rsh); - tac->rsh = NULL; - } - if (NULL != tac->fo) - { - TMH_EXCHANGES_find_exchange_cancel (tac->fo); - tac->fo = NULL; - } + TMH_check_tip_reserve_cleanup (&tac->ctr); TMH_PARSE_post_cleanup_callback (tac->json_parse_context); GNUNET_free (tac); } /** - * Function called with the result of the /reserve/status request - * for the tipping reserve. Update our database balance with the - * result. - * - * @param cls closure with a `struct TipAuthContext *' - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request - * 0 if the exchange's reply is bogus (fails to follow the protocol) - * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param[in] json original response in JSON format (useful only for diagnostics) - * @param balance current balance in the reserve, NULL on error - * @param history_length number of entries in the transaction history, 0 on error - * @param history detailed transaction history, NULL on error - */ -static void -handle_status (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history) -{ - struct TipAuthContext *tac = cls; - - tac->rsh = NULL; - if (MHD_HTTP_OK != http_status) - { - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to obtain tipping reserve status from exchange (%u/%d)\n"), - http_status, - ec); - tac->exchange_ec = ec; - MHD_resume_connection (tac->connection); - TMH_trigger_daemon (); - return; - } - - /* Update DB based on status! */ - for (unsigned int i=0;i<history_length;i++) - { - switch (history[i].type) - { - case TALER_EXCHANGE_RTT_DEPOSIT: - { - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_HashCode uuid; - struct GNUNET_TIME_Absolute expiration; - - expiration = GNUNET_TIME_absolute_add (history[i].details.in_details.timestamp, - tac->idle_reserve_expiration_time); - GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference, - history[i].details.in_details.wire_reference_size, - &uuid); - qs = db->enable_tip_reserve_TR (db->cls, - &tac->reserve_priv, - &uuid, - &history[i].amount, - expiration); - if (0 > qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Database error updating tipping reserve status: %d\n"), - qs); - } - } - break; - case TALER_EXCHANGE_RTT_WITHDRAWAL: - /* expected */ - break; - case TALER_EXCHANGE_RTT_PAYBACK: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Encountered unsupported /payback operation on tipping reserve\n")); - break; - case TALER_EXCHANGE_RTT_CLOSE: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Exchange closed reserve (due to expiration), balance calulation is likely wrong. Please create a fresh reserve.\n")); - break; - } - } - /* Finally, resume processing */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming HTTP processing\n"); - MHD_resume_connection (tac->connection); - TMH_trigger_daemon (); -} - - -/** - * Function called with the result of a #TMH_EXCHANGES_find_exchange() - * operation. - * - * @param cls closure with a `struct TipAuthContext *' - * @param eh handle to the exchange context - * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available - * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config - */ -static void -exchange_cont (void *cls, - struct TALER_EXCHANGE_Handle *eh, - const struct TALER_Amount *wire_fee, - int exchange_trusted) -{ - struct TipAuthContext *tac = cls; - struct TALER_ReservePublicKeyP reserve_pub; - const struct TALER_EXCHANGE_Keys *keys; - - tac->fo = NULL; - if (NULL == eh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to contact exchange configured for tipping!\n")); - MHD_resume_connection (tac->connection); - TMH_trigger_daemon (); - return; - } - keys = TALER_EXCHANGE_get_keys (eh); - GNUNET_assert (NULL != keys); - tac->idle_reserve_expiration_time - = keys->reserve_closing_delay; - GNUNET_CRYPTO_eddsa_key_get_public (&tac->reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - tac->rsh = TALER_EXCHANGE_reserve_status (eh, - &reserve_pub, - &handle_status, - tac); -} - - -/** * Handle a "/tip-authorize" request. * * @param rh context of the handler @@ -305,14 +142,22 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, { tac = GNUNET_new (struct TipAuthContext); tac->hc.cc = &cleanup_tac; - tac->connection = connection; + tac->ctr.connection = connection; *connection_cls = tac; } else { tac = *connection_cls; } - + if (NULL != tac->ctr.response) + { + res = MHD_queue_response (connection, + tac->ctr.response_code, + tac->ctr.response); + MHD_destroy_response (tac->ctr.response); + tac->ctr.response = NULL; + return res; + } if (GNUNET_NO == tac->parsed_json) { struct GNUNET_JSON_Specification spec[] = { @@ -336,13 +181,16 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, (NULL == tac->root) ) return MHD_YES; - if (NULL == json_object_get (tac->root, "pickup_url")) + if (NULL == json_object_get (tac->root, + "pickup_url")) { char *pickup_url = TALER_url_absolute_mhd (connection, "/public/tip-pickup", NULL); GNUNET_assert (NULL != pickup_url); - json_object_set_new (tac->root, "pickup_url", json_string (pickup_url)); + json_object_set_new (tac->root, + "pickup_url", + json_string (pickup_url)); GNUNET_free (pickup_url); } @@ -364,8 +212,8 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, "Instance `%s' not configured\n", tac->instance); return TMH_RESPONSE_reply_not_found (connection, - TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN, - "unknown instance"); + TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN, + "unknown instance"); } if (NULL == mi->tip_exchange) { @@ -373,10 +221,10 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, "Instance `%s' not configured for tipping\n", tac->instance); return TMH_RESPONSE_reply_not_found (connection, - TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP, - "exchange for tipping not configured for the instance"); + TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP, + "exchange for tipping not configured for the instance"); } - tac->reserve_priv = mi->tip_reserve; + tac->ctr.reserve_priv = mi->tip_reserve; ec = db->authorize_tip_TR (db->cls, tac->justification, &tac->amount, @@ -390,17 +238,15 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, if ( (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS == ec) && (GNUNET_NO == tac->checked_status) ) { - MHD_suspend_connection (connection); tac->checked_status = GNUNET_YES; - tac->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange, - NULL, - &exchange_cont, - tac); + tac->ctr.none_authorized = GNUNET_YES; + TMH_check_tip_reserve (&tac->ctr, + mi->tip_exchange); return MHD_YES; } /* handle irrecoverable errors */ - if (TALER_EC_NONE != (ec | tac->exchange_ec)) + if (TALER_EC_NONE != ec) { unsigned int rc; const char *msg; @@ -425,22 +271,6 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh, break; } - /* If the exchange complained earlier, we do - * override what the database returned. */ - switch (tac->exchange_ec) - { - case TALER_EC_RESERVE_STATUS_UNKNOWN: - rc = MHD_HTTP_NOT_FOUND; - msg = "Exchange does not find any reserve having this key"; - /* We override what the DB returned, as an exchange error - * is more important. */ - ec = TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN; - break; - default: - /* This makes the compiler silent. */ - break; - } - return TMH_RESPONSE_reply_rc (connection, rc, ec, diff --git a/src/backend/taler-merchant-httpd_tip-query.c b/src/backend/taler-merchant-httpd_tip-query.c @@ -29,6 +29,7 @@ #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_responses.h" #include "taler-merchant-httpd_tip-query.h" +#include "taler-merchant-httpd_tip-reserve-helper.h" /** @@ -46,75 +47,15 @@ struct TipQueryContext struct TM_HandlerContext hc; /** - * HTTP connection we are handling. - */ - struct MHD_Connection *connection; - - /** * Merchant instance to use. */ const char *instance; /** - * Handle to pending /reserve/status request. - */ - struct TALER_EXCHANGE_ReserveStatusHandle *rsh; - - /** - * Handle for operation to obtain exchange handle. - */ - struct TMH_EXCHANGES_FindOperation *fo; - - /** - * Reserve expiration time as provided by the exchange. - * Set in #exchange_cont. - */ - struct GNUNET_TIME_Relative idle_reserve_expiration_time; - - /** - * Tip amount requested. - */ - struct TALER_Amount amount_deposited; - - /** - * Tip amount requested. - */ - struct TALER_Amount amount_withdrawn; - - /** - * Amount authorized. - */ - struct TALER_Amount amount_authorized; - - /** - * Private key used by this merchant for the tipping reserve. - */ - struct TALER_ReservePrivateKeyP reserve_priv; - - /** - * No tips were authorized yet. - */ - int none_authorized; - - /** - * Response to return, NULL if we don't have one yet. - */ - struct MHD_Response *response; - - /** - * HTTP status code to use for the reply, i.e 200 for "OK". - * Special value UINT_MAX is used to indicate hard errors - * (no reply, return #MHD_NO). + * Context for checking the tipping reserve's status. */ - unsigned int response_code; + struct CheckTipReserve ctr; - /** - * #GNUNET_NO if the @e connection was not suspended, - * #GNUNET_YES if the @e connection was suspended, - * #GNUNET_SYSERR if @e connection was resumed to as - * part of #MH_force_pc_resume during shutdown. - */ - int suspended; }; @@ -128,288 +69,52 @@ cleanup_tqc (struct TM_HandlerContext *hc) { struct TipQueryContext *tqc = (struct TipQueryContext *) hc; - if (NULL != tqc->rsh) - { - TALER_EXCHANGE_reserve_status_cancel (tqc->rsh); - tqc->rsh = NULL; - } - if (NULL != tqc->fo) - { - TMH_EXCHANGES_find_exchange_cancel (tqc->fo); - tqc->fo = NULL; - } + TMH_check_tip_reserve_cleanup (&tqc->ctr); GNUNET_free (tqc); } /** - * Resume the given context and send the given response. Stores the response - * in the @a pc and signals MHD to resume the connection. Also ensures MHD - * runs immediately. + * We've been resumed after processing the reserve data from the + * exchange without error. Generate the final response. * - * @param pc payment context - * @param response_code response code to use - * @param response response data to send back + * @param tqc context for which to generate the response. */ -static void -resume_with_response (struct TipQueryContext *tqc, - unsigned int response_code, - struct MHD_Response *response) +static int +generate_final_response (struct TipQueryContext *tqc) { - tqc->response_code = response_code; - tqc->response = response; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /tip-query response (%u)\n", - response_code); - GNUNET_assert (GNUNET_YES == tqc->suspended); - tqc->suspended = GNUNET_NO; - MHD_resume_connection (tqc->connection); - TMH_trigger_daemon (); /* we resumed, kick MHD */ -} - - -/** - * Function called with the result of the /reserve/status request - * for the tipping reserve. Update our database balance with the - * result. - * - * @param cls closure with a `struct TipAuthContext *' - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request - * 0 if the exchange's reply is bogus (fails to follow the protocol) - * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param[in] json original response in JSON format (useful only for diagnostics) - * @param balance current balance in the reserve, NULL on error - * @param history_length number of entries in the transaction history, 0 on error - * @param history detailed transaction history, NULL on error - */ -static void -handle_status (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history) -{ - struct TipQueryContext *tqc = cls; - struct GNUNET_TIME_Absolute reserve_expiration = GNUNET_TIME_UNIT_ZERO_ABS; - - tqc->rsh = NULL; - if (MHD_HTTP_OK != http_status) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_SERVICE_UNAVAILABLE, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_STATUS_FAILED_EXCHANGE_DOWN, - "Unable to obtain reserve status from exchange")); - return; - } - - if (0 == history_length) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_SERVICE_UNAVAILABLE, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_FAILED_EMPTY, - "Exchange returned empty reserve history")); - return; - } - - if (TALER_EXCHANGE_RTT_DEPOSIT != history[0].type) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_SERVICE_UNAVAILABLE, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_NO_DEPOSIT, - "Exchange returned invalid reserve history")); - return; - } + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + struct TALER_Amount amount_available; - if (GNUNET_OK != - TALER_amount_get_zero (history[0].amount.currency, - &tqc->amount_withdrawn)) + GNUNET_CRYPTO_eddsa_key_get_public (&tqc->ctr.reserve_priv.eddsa_priv, + &reserve_pub); + if (GNUNET_SYSERR == + TALER_amount_subtract (&amount_available, + &tqc->ctr.amount_deposited, + &tqc->ctr.amount_withdrawn)) { GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_SERVICE_UNAVAILABLE, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_CURRENCY, - "Exchange returned invalid reserve history")); - return; - } - - if (0 != strcasecmp (TMH_currency, - history[0].amount.currency)) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_SERVICE_UNAVAILABLE, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISSMATCH, - "Exchange currency unexpected")); - return; - } - - if (GNUNET_YES == tqc->none_authorized) - tqc->amount_authorized = tqc->amount_withdrawn; - tqc->amount_deposited = tqc->amount_withdrawn; - - /* Update DB based on status! */ - for (unsigned int i=0;i<history_length;i++) - { - switch (history[i].type) - { - case TALER_EXCHANGE_RTT_DEPOSIT: - { - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_HashCode uuid; - struct GNUNET_TIME_Absolute deposit_expiration; - - deposit_expiration = GNUNET_TIME_absolute_add (history[i].details.in_details.timestamp, - tqc->idle_reserve_expiration_time); - /* We're interested in the latest DEPOSIT timestamp, since this determines the - * reserve's expiration date. Note that the history isn't chronologically ordered. */ - reserve_expiration = GNUNET_TIME_absolute_max (reserve_expiration, deposit_expiration); - GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference, - history[i].details.in_details.wire_reference_size, - &uuid); - qs = db->enable_tip_reserve_TR (db->cls, - &tqc->reserve_priv, - &uuid, - &history[i].amount, - deposit_expiration); - if (GNUNET_OK != - TALER_amount_add (&tqc->amount_deposited, - &tqc->amount_deposited, - &history[i].amount)) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_DEPOSIT, - "Exchange returned invalid reserve history (amount overflow)")); - return; - } - - if (0 > qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Database error updating tipping reserve status: %d\n"), - qs); - } - } - break; - case TALER_EXCHANGE_RTT_WITHDRAWAL: - if (GNUNET_OK != - TALER_amount_add (&tqc->amount_withdrawn, - &tqc->amount_withdrawn, - &history[i].amount)) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_WITHDRAW, - "Exchange returned invalid reserve history (amount overflow)")); - return; - } - break; - case TALER_EXCHANGE_RTT_PAYBACK: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Encountered unsupported /payback operation on tipping reserve\n")); - break; - case TALER_EXCHANGE_RTT_CLOSE: - /* We count 'closing' amounts just like withdrawals */ - if (GNUNET_OK != - TALER_amount_add (&tqc->amount_withdrawn, - &tqc->amount_withdrawn, - &history[i].amount)) - { - GNUNET_break_op (0); - resume_with_response (tqc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_CLOSED, - "Exchange returned invalid reserve history (amount overflow)")); - return; - } - break; - } - } - - { - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - struct TALER_Amount amount_available; - - GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv, - &reserve_pub); - if (GNUNET_SYSERR == - TALER_amount_subtract (&amount_available, - &tqc->amount_deposited, - &tqc->amount_withdrawn)) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "amount overflow, deposited %s but withdrawn %s\n", - TALER_amount_to_string (&tqc->amount_deposited), - TALER_amount_to_string (&tqc->amount_withdrawn)); - - resume_with_response (tqc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_INCONSISTENT, - "Exchange returned invalid reserve history (amount overflow)")); - } - resume_with_response (tqc, - MHD_HTTP_OK, - TMH_RESPONSE_make_json_pack ("{s:o, s:o, s:o, s:o, s:o}", - "reserve_pub", - GNUNET_JSON_from_data_auto (&reserve_pub), - "reserve_expiration", - GNUNET_JSON_from_time_abs (reserve_expiration), - "amount_authorized", - TALER_JSON_from_amount (&tqc->amount_authorized), - "amount_picked_up", - TALER_JSON_from_amount (&tqc->amount_withdrawn), - "amount_available", - TALER_JSON_from_amount (&amount_available))); - } -} - - -/** - * Function called with the result of a #TMH_EXCHANGES_find_exchange() - * operation. - * - * @param cls closure with a `struct TipQueryContext *` - * @param eh handle to the exchange context - * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available - * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config - */ -static void -exchange_cont (void *cls, - struct TALER_EXCHANGE_Handle *eh, - const struct TALER_Amount *wire_fee, - int exchange_trusted) -{ - struct TipQueryContext *tqc = cls; - struct TALER_ReservePublicKeyP reserve_pub; - const struct TALER_EXCHANGE_Keys *keys; - - tqc->fo = NULL; - if (NULL == eh) - { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to contact exchange configured for tipping!\n")); - MHD_resume_connection (tqc->connection); - TMH_trigger_daemon (); - return; - } - keys = TALER_EXCHANGE_get_keys (eh); - GNUNET_assert (NULL != keys); - tqc->idle_reserve_expiration_time - = keys->reserve_closing_delay; - GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - tqc->rsh = TALER_EXCHANGE_reserve_status (eh, - &reserve_pub, - &handle_status, - tqc); + "amount overflow, deposited %s but withdrawn %s\n", + TALER_amount_to_string (&tqc->ctr.amount_deposited), + TALER_amount_to_string (&tqc->ctr.amount_withdrawn)); + return TMH_RESPONSE_reply_internal_error (tqc->ctr.connection, + TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_INCONSISTENT, + "Exchange returned invalid reserve history (amount overflow)"); + } + return TMH_RESPONSE_reply_json_pack (tqc->ctr.connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o, s:o, s:o}", + "reserve_pub", + GNUNET_JSON_from_data_auto (&reserve_pub), + "reserve_expiration", + GNUNET_JSON_from_time_abs (tqc->ctr.reserve_expiration), + "amount_authorized", + TALER_JSON_from_amount (&tqc->ctr.amount_authorized), + "amount_picked_up", + TALER_JSON_from_amount (&tqc->ctr.amount_withdrawn), + "amount_available", + TALER_JSON_from_amount (&amount_available)); } @@ -438,7 +143,7 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, { tqc = GNUNET_new (struct TipQueryContext); tqc->hc.cc = &cleanup_tqc; - tqc->connection = connection; + tqc->ctr.connection = connection; *connection_cls = tqc; } else @@ -446,26 +151,34 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, tqc = *connection_cls; } - if (0 != tqc->response_code) + if (0 != tqc->ctr.response_code) { /* We are *done* processing the request, just queue the response (!) */ - if (UINT_MAX == tqc->response_code) + if (UINT_MAX == tqc->ctr.response_code) { GNUNET_break (0); return MHD_NO; /* hard error */ } res = MHD_queue_response (connection, - tqc->response_code, - tqc->response); - MHD_destroy_response (tqc->response); - tqc->response = NULL; + tqc->ctr.response_code, + tqc->ctr.response); + MHD_destroy_response (tqc->ctr.response); + tqc->ctr.response = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queueing response (%u) for /tip-query (%s).\n", - (unsigned int) tqc->response_code, - res ? "OK" : "FAILED"); + "Queueing response (%u) for /tip-query (%s).\n", + (unsigned int) tqc->ctr.response_code, + res ? "OK" : "FAILED"); return res; } + if (NULL != tqc->instance) + { + /* We've been here before, so TMH_check_tip_reserve() must have + finished and left the result for us. Finish processing. */ + return generate_final_response (tqc); + } + + /* No error yet, so first time here, let's query the exchange */ tqc->instance = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "instance"); @@ -481,8 +194,8 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, "Instance `%s' not configured\n", tqc->instance); return TMH_RESPONSE_reply_not_found (connection, - TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN, - "unknown instance"); + TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN, + "unknown instance"); } if (NULL == mi->tip_exchange) { @@ -493,7 +206,7 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP, "exchange for tipping not configured for the instance"); } - tqc->reserve_priv = mi->tip_reserve; + tqc->ctr.reserve_priv = mi->tip_reserve; { int qs; @@ -501,8 +214,8 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, { db->preflight (db->cls); qs = db->get_authorized_tip_amount (db->cls, - &tqc->reserve_priv, - &tqc->amount_authorized); + &tqc->ctr.reserve_priv, + &tqc->ctr.amount_authorized); if (GNUNET_DB_STATUS_SOFT_ERROR != qs) break; } @@ -518,17 +231,12 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh, { /* we'll set amount_authorized to zero later once we know the currency */ - tqc->none_authorized = GNUNET_YES; + tqc->ctr.none_authorized = GNUNET_YES; } } - MHD_suspend_connection (connection); - tqc->suspended = GNUNET_YES; - - tqc->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange, - NULL, - &exchange_cont, - tqc); + TMH_check_tip_reserve (&tqc->ctr, + mi->tip_exchange); return MHD_YES; } diff --git a/src/backend/taler-merchant-httpd_tip-reserve-helper.c b/src/backend/taler-merchant-httpd_tip-reserve-helper.c @@ -0,0 +1,329 @@ +/* + This file is part of TALER + (C) 2018--2019 Taler Systems SA + + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file backend/taler-merchant-httpd_tip-reserve-helper.c + * @brief helper functions to check the status of a tipping reserve + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_tip-reserve-helper.h" + + +/** + * Resume the given context and send the given response. Stores the response + * in the @a ctr and signals MHD to resume the connection. Also ensures MHD + * runs immediately. + * + * @param ctr tip reserve query helper context + * @param response_code response code to use + * @param response response data to send back + */ +static void +resume_with_response (struct CheckTipReserve *ctr, + unsigned int response_code, + struct MHD_Response *response) +{ + ctr->response_code = response_code; + ctr->response = response; + GNUNET_assert (GNUNET_YES == ctr->suspended); + ctr->suspended = GNUNET_NO; + MHD_resume_connection (ctr->connection); + TMH_trigger_daemon (); /* we resumed, kick MHD */ +} + + +/** + * Function called with the result of the /reserve/status request + * for the tipping reserve. Update our database balance with the + * result. + * + * @param cls closure with a `struct CheckTipReserve *' + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the exchange's reply is bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param[in] json original response in JSON format (useful only for diagnostics) + * @param balance current balance in the reserve, NULL on error + * @param history_length number of entries in the transaction history, 0 on error + * @param history detailed transaction history, NULL on error + */ +static void +handle_status (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_EXCHANGE_ReserveHistory *history) +{ + struct CheckTipReserve *ctr = cls; + + ctr->rsh = NULL; + ctr->reserve_expiration = GNUNET_TIME_UNIT_ZERO_ABS; + if (MHD_HTTP_NOT_FOUND == http_status) + { + resume_with_response (ctr, + MHD_HTTP_NOT_FOUND, + TMH_RESPONSE_make_error (ec, + "Reserve unknown at exchange")); + return; + } + if (MHD_HTTP_OK != http_status) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (ec, + "Exchange returned error code for reserve status")); + return; + } + + if (0 == history_length) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_FAILED_EMPTY, + "Exchange returned empty reserve history")); + return; + } + + if (TALER_EXCHANGE_RTT_DEPOSIT != history[0].type) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_NO_DEPOSIT, + "Exchange returned invalid reserve history")); + return; + } + + if (GNUNET_OK != + TALER_amount_get_zero (history[0].amount.currency, + &ctr->amount_withdrawn)) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_CURRENCY, + "Exchange returned invalid reserve history")); + return; + } + + if (0 != strcasecmp (TMH_currency, + history[0].amount.currency)) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISSMATCH, + "Exchange currency unexpected")); + return; + } + + if (GNUNET_YES == ctr->none_authorized) + ctr->amount_authorized = ctr->amount_withdrawn; + ctr->amount_deposited = ctr->amount_withdrawn; + + /* Update DB based on status! */ + for (unsigned int i=0;i<history_length;i++) + { + switch (history[i].type) + { + case TALER_EXCHANGE_RTT_DEPOSIT: + { + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_HashCode uuid; + struct GNUNET_TIME_Absolute deposit_expiration; + + deposit_expiration = GNUNET_TIME_absolute_add (history[i].details.in_details.timestamp, + ctr->idle_reserve_expiration_time); + /* We're interested in the latest DEPOSIT timestamp, since this determines the + * reserve's expiration date. Note that the history isn't chronologically ordered. */ + ctr->reserve_expiration = GNUNET_TIME_absolute_max (ctr->reserve_expiration, + deposit_expiration); + GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference, + history[i].details.in_details.wire_reference_size, + &uuid); + qs = db->enable_tip_reserve_TR (db->cls, + &ctr->reserve_priv, + &uuid, + &history[i].amount, + deposit_expiration); + if (GNUNET_OK != + TALER_amount_add (&ctr->amount_deposited, + &ctr->amount_deposited, + &history[i].amount)) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_DEPOSIT, + "Exchange returned invalid reserve history (amount overflow)")); + return; + } + + if (0 > qs) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Database error updating tipping reserve status: %d\n"), + qs); + } + } + break; + case TALER_EXCHANGE_RTT_WITHDRAWAL: + if (GNUNET_OK != + TALER_amount_add (&ctr->amount_withdrawn, + &ctr->amount_withdrawn, + &history[i].amount)) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_WITHDRAW, + "Exchange returned invalid reserve history (amount overflow)")); + return; + } + break; + case TALER_EXCHANGE_RTT_PAYBACK: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Encountered unsupported /payback operation on tipping reserve\n")); + /* FIXME: probably should count these like deposits!? */ + break; + case TALER_EXCHANGE_RTT_CLOSE: + /* We count 'closing' amounts just like withdrawals */ + if (GNUNET_OK != + TALER_amount_add (&ctr->amount_withdrawn, + &ctr->amount_withdrawn, + &history[i].amount)) + { + GNUNET_break_op (0); + resume_with_response (ctr, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_CLOSED, + "Exchange returned invalid reserve history (amount overflow)")); + return; + } + break; + } + } + + /* normal, non-error continuation */ + resume_with_response (ctr, + 0, + NULL); +} + + +/** + * Function called with the result of a #TMH_EXCHANGES_find_exchange() + * operation. Given the exchange handle, we will then interrogate + * the exchange about the status of the tipping reserve. + * + * @param cls closure with a `struct CheckTipReserve *` + * @param eh handle to the exchange context + * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available + * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config + */ +static void +exchange_cont (void *cls, + struct TALER_EXCHANGE_Handle *eh, + const struct TALER_Amount *wire_fee, + int exchange_trusted) +{ + struct CheckTipReserve *ctr = cls; + struct TALER_ReservePublicKeyP reserve_pub; + const struct TALER_EXCHANGE_Keys *keys; + + ctr->fo = NULL; + if (NULL == eh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to contact exchange configured for tipping!\n")); + resume_with_response (ctr, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_error (TALER_EC_TIP_QUERY_RESERVE_STATUS_FAILED_EXCHANGE_DOWN, + "Unable to obtain /keys from exchange")); + return; + } + keys = TALER_EXCHANGE_get_keys (eh); + GNUNET_assert (NULL != keys); + ctr->idle_reserve_expiration_time + = keys->reserve_closing_delay; + GNUNET_CRYPTO_eddsa_key_get_public (&ctr->reserve_priv.eddsa_priv, + &reserve_pub.eddsa_pub); + ctr->rsh = TALER_EXCHANGE_reserve_status (eh, + &reserve_pub, + &handle_status, + ctr); +} + + +/** + * Check the status of the given reserve at the given exchange. + * Suspends the MHD connection while this is happening and resumes + * processing once we know the reserve status (or once an error + * code has been determined). + * + * @param[in,out] ctr context for checking the reserve status + * @param tip_exchange the URL of the exchange to query + */ +void +TMH_check_tip_reserve (struct CheckTipReserve *ctr, + const char *tip_exchange) +{ + MHD_suspend_connection (ctr->connection); + ctr->suspended = GNUNET_YES; + ctr->fo = TMH_EXCHANGES_find_exchange (tip_exchange, + NULL, + &exchange_cont, + ctr); + if (NULL == ctr->fo) + { + GNUNET_break (0); + resume_with_response (ctr, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TMH_RESPONSE_make_error (TALER_EC_INTERNAL_INVARIANT_FAILURE, + "Unable to find exchange handle")); + } +} + + +/** + * Clean up any state that might be left in @a ctr. + * + * @param[in] context to clean up + */ +void +TMH_check_tip_reserve_cleanup (struct CheckTipReserve *ctr) +{ + if (NULL != ctr->rsh) + { + TALER_EXCHANGE_reserve_status_cancel (ctr->rsh); + ctr->rsh = NULL; + } + if (NULL != ctr->fo) + { + TMH_EXCHANGES_find_exchange_cancel (ctr->fo); + ctr->fo = NULL; + } + if (NULL != ctr->response) + { + MHD_destroy_response (ctr->response); + ctr->response = NULL; + } +} + +/* end of taler-merchant-httpd_tip-reserve-helper.c */ diff --git a/src/backend/taler-merchant-httpd_tip-reserve-helper.h b/src/backend/taler-merchant-httpd_tip-reserve-helper.h @@ -0,0 +1,137 @@ +/* + This file is part of TALER + (C) 2018--2019 Taler Systems SA + + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file backend/taler-merchant-httpd_tip-reserve-helper.h + * @brief helper functions to check the status of a tipping reserve + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_TIP_RESERVE_HELPER_H +#define TALER_MERCHANT_HTTPD_TIP_RESERVE_HELPER_H +#include <jansson.h> +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler-merchant-httpd.h" +#include "taler-merchant-httpd_mhd.h" +#include "taler-merchant-httpd_parsing.h" +#include "taler-merchant-httpd_exchanges.h" +#include "taler-merchant-httpd_responses.h" +#include "taler-merchant-httpd_tip-reserve-helper.h" + + +/** + * Context with input, output and internal state for + * #TMH_check_tip_reserve() and #TMH_check_tip_reserve_cleanup(). + */ +struct CheckTipReserve +{ + /** + * Input: MHD connection we should resume when finished + */ + struct MHD_Connection *connection; + + /** + * Input: private key of the reserve. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Output: Set to delay after which the reserve will expire if idle. + */ + struct GNUNET_TIME_Relative idle_reserve_expiration_time; + + /** + * Internal: exchange find operation. + */ + struct TMH_EXCHANGES_FindOperation *fo; + + /** + * Internal: reserve status operation. + */ + struct TALER_EXCHANGE_ReserveStatusHandle *rsh; + + /** + * Output: response object to return (on error only) + */ + struct MHD_Response *response; + + /** + * Output: Total amount deposited into the reserve. + */ + struct TALER_Amount amount_deposited; + + /** + * Output: total tip amount requested. + */ + struct TALER_Amount amount_withdrawn; + + /** + * Input: total amount authorized. + */ + struct TALER_Amount amount_authorized; + + /** + * Output: set to the time when the reserve will expire + */ + struct GNUNET_TIME_Absolute reserve_expiration; + + /** + * Output: HTTP status code to return (on error only) + */ + unsigned int response_code; + + /** + * Input: Set to #GNUNET_NO if no tips were authorized yet. + * Used to know that @e amount_authorized is not yet initialized + * and in that case the helper will set it to zero (once we know + * the currency). + */ + int none_authorized; + + /** + * Internal: Is the @e connection currently suspended? + * #GNUNET_NO if the @e connection was not suspended, + * #GNUNET_YES if the @e connection was suspended, + * #GNUNET_SYSERR if @e connection was resumed to as + * part of #MH_force_pc_resume during shutdown. + */ + int suspended; + +}; + + +/** + * Check the status of the given reserve at the given exchange. + * Suspends the MHD connection while this is happening and resumes + * processing once we know the reserve status (or once an error + * code has been determined). + * + * @param[in,out] ctr context for checking the reserve status + * @param tip_exchange the URL of the exchange to query + */ +void +TMH_check_tip_reserve (struct CheckTipReserve *ctr, + const char *tip_exchange); + + +/** + * Clean up any state that might be left in @a ctr. + * + * @param[in] context to clean up + */ +void +TMH_check_tip_reserve_cleanup (struct CheckTipReserve *ctr); + +#endif diff --git a/src/lib/test_merchant_api_new.c b/src/lib/test_merchant_api_new.c @@ -511,7 +511,7 @@ run (void *cls, /** * The following block will (1) create a new * reserve, then (2) a proposal, then (3) pay for - * it, and finally (4) attempt to pick up a refund + * it, and finally (4) attempt to pick up a refund * from it without any increasing taking place * in the first place. **/ @@ -641,7 +641,7 @@ run (void *cls, "nulltip", "tip 2", "EUR:5.01", - TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN), + TALER_EC_RESERVE_STATUS_UNKNOWN), TALER_TESTING_cmd_tip_query ("query-tip-1", merchant_url,