diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-05-18 19:11:08 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-05-18 19:11:08 +0200 |
commit | 49bb51daf5486817c3adc8a63137de1ec9d98589 (patch) | |
tree | c1b3151faf42ab2abdb64b46b4e10c0330183b84 | |
parent | 1792cbaa740194e7aebb051f7ce49b3d17976648 (diff) | |
download | merchant-49bb51daf5486817c3adc8a63137de1ec9d98589.tar.gz merchant-49bb51daf5486817c3adc8a63137de1ec9d98589.tar.bz2 merchant-49bb51daf5486817c3adc8a63137de1ec9d98589.zip |
implement POST /reserves
-rw-r--r-- | src/backend/Makefile.am | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 11 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_auditors.c | 6 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_auditors.h | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 111 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.h | 6 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_get-orders-ID.c | 7 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-abort.c | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-pay.c | 7 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-reserves.c | 288 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-reserves.h | 50 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-transfers.c | 6 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 26 |
13 files changed, 465 insertions, 67 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 3127ab24..bba50887 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -53,6 +53,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-post-products.h \ taler-merchant-httpd_private-post-products-ID-lock.c \ taler-merchant-httpd_private-post-products-ID-lock.h \ + taler-merchant-httpd_private-post-reserves.c \ + taler-merchant-httpd_private-post-reserves.h \ taler-merchant-httpd_private-post-orders-ID-refund.c \ taler-merchant-httpd_private-post-orders-ID-refund.h \ taler-merchant-httpd_private-post-orders.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index b280386b..92334bbb 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -42,10 +42,11 @@ #include "taler-merchant-httpd_private-patch-instances-ID.h" #include "taler-merchant-httpd_private-patch-products-ID.h" #include "taler-merchant-httpd_private-post-instances.h" -#include "taler-merchant-httpd_private-post-products.h" -#include "taler-merchant-httpd_private-post-products-ID-lock.h" #include "taler-merchant-httpd_private-post-orders.h" #include "taler-merchant-httpd_private-post-orders-ID-refund.h" +#include "taler-merchant-httpd_private-post-products.h" +#include "taler-merchant-httpd_private-post-products-ID-lock.h" +#include "taler-merchant-httpd_private-post-reserves.h" #include "taler-merchant-httpd_private-post-transfers.h" #include "taler-merchant-httpd_post-orders-ID-abort.h" #include "taler-merchant-httpd_post-orders-ID-claim.h" @@ -833,6 +834,12 @@ url_handler (void *cls, .have_id_segment = true, .handler = &TMH_private_post_orders_ID_refund }, + /* POST /reserves: */ + { + .url_prefix = "/reserves", + .method = MHD_HTTP_METHOD_POST, + .handler = &TMH_private_post_reserves + }, /* POST /transfers: */ { .url_prefix = "/transfers", diff --git a/src/backend/taler-merchant-httpd_auditors.c b/src/backend/taler-merchant-httpd_auditors.c index 225fa157..05690a99 100644 --- a/src/backend/taler-merchant-httpd_auditors.c +++ b/src/backend/taler-merchant-httpd_auditors.c @@ -69,7 +69,7 @@ json_t *j_auditors; * * @param mh exchange issuing @a dk * @param dk a denomination issued by @a mh - * @param exchange_trusted #GNUNET_YES if the exchange of @a dk is trusted by config + * @param exchange_trusted true if the exchange of @a dk is trusted by config * @param[out] hc HTTP status code to return (on error) * @param[out] ec Taler error code to return (on error) * @return #GNUNET_OK @@ -77,7 +77,7 @@ json_t *j_auditors; int TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh, const struct TALER_EXCHANGE_DenomPublicKey *dk, - int exchange_trusted, + bool exchange_trusted, unsigned int *hc, enum TALER_ErrorCode *ec) { @@ -92,7 +92,7 @@ TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh, *ec = TALER_EC_PAY_DENOMINATION_DEPOSIT_EXPIRED; return GNUNET_SYSERR; /* expired */ } - if (GNUNET_YES == exchange_trusted) + if (exchange_trusted) { *ec = TALER_EC_NONE; *hc = MHD_HTTP_OK; diff --git a/src/backend/taler-merchant-httpd_auditors.h b/src/backend/taler-merchant-httpd_auditors.h index 4a1a0b61..4786b804 100644 --- a/src/backend/taler-merchant-httpd_auditors.h +++ b/src/backend/taler-merchant-httpd_auditors.h @@ -53,7 +53,7 @@ TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg); * * @param mh exchange issuing @a dk * @param dk a denomination issued by @a mh - * @param exchange_trusted #GNUNET_YES if the exchange of @a dk is trusted by config + * @param exchange_trusted true if the exchange of @a dk is trusted by config * @param[out] hc set to the HTTP status code to return * @param[out] ec set to the Taler error code to return * @return #GNUNET_OK on success @@ -61,7 +61,7 @@ TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg); int TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh, const struct TALER_EXCHANGE_DenomPublicKey *dk, - int exchange_trusted, + bool exchange_trusted, unsigned int *hc, enum TALER_ErrorCode *ec); diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index fe4085d0..24b5202e 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -130,6 +130,11 @@ struct FeesByWireMethod char *wire_method; /** + * Full payto URI of the exchange. + */ + char *payto_uri; + + /** * Applicable fees, NULL if unknown/error. */ struct TALER_EXCHANGE_WireAggregateFees *af; @@ -220,18 +225,18 @@ struct Exchange struct GNUNET_SCHEDULER_Task *retry_task; /** - * #GNUNET_YES to indicate that there is an ongoing + * true to indicate that there is an ongoing * transfer we are waiting for, - * #GNUNET_NO to indicate that key data is up-to-date. + * false to indicate that key data is up-to-date. */ - int pending; + bool pending; /** - * #GNUNET_YES if this exchange is from our configuration and - * explicitly trusted, #GNUNET_NO if we need to check each + * true if this exchange is from our configuration and + * explicitly trusted, false if we need to check each * key to be sure it is trusted. */ - int trusted; + bool trusted; }; @@ -322,10 +327,12 @@ retry_exchange (void *cls) /** * Function called with information about the wire fees * for each wire method. Stores the wire fees with the - * exchange for laster use. + * exchange for later use. * * @param cls closure - * @param wire_method name of the wire method (i.e. "sepa") + * @param master_pub public key of the exchange + * @param wire_method name of the wire method (i.e. "iban") + * @param payto_uri full payto URI of the exchange * @param fees fee structure for this method * @return #TALER_EC_NONE on success */ @@ -333,6 +340,7 @@ static enum TALER_ErrorCode process_wire_fees (struct Exchange *exchange, const struct TALER_MasterPublicKeyP *master_pub, const char *wire_method, + const char *payto_uri, const struct TALER_EXCHANGE_WireAggregateFees *fees) { struct FeesByWireMethod *f; @@ -347,6 +355,7 @@ process_wire_fees (struct Exchange *exchange, { f = GNUNET_new (struct FeesByWireMethod); f->wire_method = GNUNET_strdup (wire_method); + f->payto_uri = GNUNET_strdup (payto_uri); GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head, exchange->wire_fees_tail, f); @@ -443,7 +452,7 @@ process_wire_fees (struct Exchange *exchange, /** * Function called with information about the wire accounts - * of the exchanage. Stores the wire fees with the + * of the exchange. Stores the wire fees with the * exchange for laster use. * * @param exchange the exchange @@ -473,6 +482,7 @@ process_wire_accounts (struct Exchange *exchange, ec = process_wire_fees (exchange, master_pub, method, + accounts[i].payto_uri, accounts[i].fees); GNUNET_free (method); if (TALER_EC_NONE != ec) @@ -490,7 +500,7 @@ process_wire_accounts (struct Exchange *exchange, * @param wire_method the wire method we want the fees for * @return NULL if we do not have fees for this method yet */ -static struct TALER_EXCHANGE_WireAggregateFees * +static struct FeesByWireMethod * get_wire_fees (struct Exchange *exchange, struct GNUNET_TIME_Absolute now, const char *wire_method) @@ -511,7 +521,7 @@ get_wire_fees (struct Exchange *exchange, fbw->af = af->next; GNUNET_free (af); } - return af; + return fbw; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange supports `%s' as a wire method (but we do not use that one)\n", @@ -527,57 +537,55 @@ get_wire_fees (struct Exchange *exchange, * the callback. * * @param exchange the exchange to check for pending find operations - * @return #GNUNET_YES if we need /wire data from @a exchange + * @return true if we need /wire data from @a exchange */ -static int +static bool process_find_operations (struct Exchange *exchange) { struct TMH_EXCHANGES_FindOperation *fn; struct GNUNET_TIME_Absolute now; - int need_wire; + bool need_wire; now = GNUNET_TIME_absolute_get (); - need_wire = GNUNET_NO; + need_wire = false; for (struct TMH_EXCHANGES_FindOperation *fo = exchange->fo_head; NULL != fo; fo = fn) { - const struct TALER_Amount *wire_fee; + struct FeesByWireMethod *fbw; fn = fo->next; if (NULL != fo->wire_method) { - struct TALER_EXCHANGE_WireAggregateFees *af; - /* Find fee structure for our wire method */ - af = get_wire_fees (exchange, - now, - fo->wire_method); - if (NULL == af) + fbw = get_wire_fees (exchange, + now, + fo->wire_method); + if (NULL == fbw) { - need_wire = GNUNET_YES; + need_wire = true; GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Exchange does not support `%s' wire method (will retry later)\n", fo->wire_method); + fbw = NULL; continue; } - if (af->start_date.abs_value_us > now.abs_value_us) + if (fbw->af->start_date.abs_value_us > now.abs_value_us) { /* Disagreement on the current time */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Exchange's earliest fee is %s adhead of our time. Clock skew issue?\n", GNUNET_STRINGS_relative_time_to_string ( - GNUNET_TIME_absolute_get_remaining (af->start_date), + GNUNET_TIME_absolute_get_remaining (fbw->af->start_date), GNUNET_YES)); + fbw = NULL; continue; } - /* found fee, great! */ - wire_fee = &af->wire_fee; } else { /* no wire transfer method given, so we yield no fee */ - wire_fee = NULL; + fbw = NULL; } { struct TALER_EXCHANGE_HttpResponse hr = { @@ -587,7 +595,8 @@ process_find_operations (struct Exchange *exchange) fo->fc (fo->fc_cls, &hr, exchange->conn, - wire_fee, + (NULL != fbw) ? fbw->payto_uri : NULL, + (NULL != fbw) ? &fbw->af->wire_fee : NULL, exchange->trusted); } TMH_EXCHANGES_find_exchange_cancel (fo); @@ -649,6 +658,7 @@ handle_wire_data (void *cls, hr, NULL, NULL, + NULL, GNUNET_NO); TMH_EXCHANGES_find_exchange_cancel (fo); } @@ -677,13 +687,13 @@ handle_wire_data (void *cls, &hrx, NULL, NULL, + NULL, GNUNET_NO); TMH_EXCHANGES_find_exchange_cancel (fo); } return; } - if ( (GNUNET_YES == - process_find_operations (exchange)) && + if ( (process_find_operations (exchange)) && (NULL == exchange->wire_task) && (NULL == exchange->wire_request) ) { @@ -725,9 +735,8 @@ wire_task_cb (void *cls) struct Exchange *exchange = cls; exchange->wire_task = NULL; - GNUNET_assert (GNUNET_NO == exchange->pending); - if (GNUNET_YES != - process_find_operations (exchange)) + GNUNET_assert (! exchange->pending); + if (! process_find_operations (exchange)) return; /* no more need */ GNUNET_assert (NULL == exchange->wire_request); exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn, @@ -751,7 +760,7 @@ fail_and_retry (struct Exchange *exchange, { struct TMH_EXCHANGES_FindOperation *fo; - exchange->pending = GNUNET_YES; + exchange->pending = true; if (NULL != exchange->wire_request) { TALER_EXCHANGE_wire_cancel (exchange->wire_request); @@ -768,6 +777,7 @@ fail_and_retry (struct Exchange *exchange, hr, NULL, NULL, + NULL, GNUNET_NO); TMH_EXCHANGES_find_exchange_cancel (fo); } @@ -833,7 +843,7 @@ keys_mgmt_cb (void *cls, compat); return; } - if ( (GNUNET_YES == exchange->trusted) && + if ( (exchange->trusted) && (0 != GNUNET_memcmp (&exchange->master_pub, &keys->master_pub)) ) { @@ -841,9 +851,9 @@ keys_mgmt_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n", exchange->url); - exchange->trusted = GNUNET_NO; + exchange->trusted = false; } - if (GNUNET_NO == exchange->trusted) + if (! exchange->trusted) exchange->master_pub = keys->master_pub; if (0 != (TALER_EXCHANGE_VC_NEWER & compat)) @@ -900,9 +910,8 @@ keys_mgmt_cb (void *cls, = GNUNET_SCHEDULER_add_delayed (delay, &retry_exchange, exchange); - exchange->pending = GNUNET_NO; - if ( (GNUNET_YES == - process_find_operations (exchange)) && + exchange->pending = false; + if ( (process_find_operations (exchange)) && (NULL == exchange->wire_request) && (NULL == exchange->wire_task) ) { @@ -927,10 +936,9 @@ return_result (void *cls) struct Exchange *exchange = fo->my_exchange; fo->at = NULL; - if ( (GNUNET_YES == - process_find_operations (exchange)) && + if ( (process_find_operations (exchange)) && (NULL == exchange->wire_request) && - (GNUNET_NO == exchange->pending) && + (! exchange->pending) && (NULL != exchange->wire_task) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1000,7 +1008,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, /* This is a new exchange */ exchange = GNUNET_new (struct Exchange); exchange->url = GNUNET_strdup (chosen_exchange); - exchange->pending = GNUNET_YES; + exchange->pending = true; GNUNET_CONTAINER_DLL_insert (exchange_head, exchange_tail, exchange); @@ -1039,7 +1047,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, } - if ( (GNUNET_YES != exchange->pending) && + if ( (! exchange->pending) && ( (NULL == fo->wire_method) || (NULL != get_wire_fees (exchange, now, @@ -1056,14 +1064,14 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, /* If new or resumed, (re)try fetching /keys */ if ( (NULL == exchange->conn) && (NULL == exchange->retry_task) && - (GNUNET_YES == exchange->pending) ) + (exchange->pending) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Do not have current key data. Will request /keys now\n"); exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange, exchange); } - else if ( (GNUNET_NO == exchange->pending) && + else if ( (! exchange->pending) && (NULL == exchange->wire_task) && (NULL == exchange->wire_request) ) { @@ -1167,7 +1175,7 @@ accept_exchanges (void *cls, &exchange->master_pub. eddsa_pub)) { - exchange->trusted = GNUNET_YES; + exchange->trusted = true; } else { @@ -1188,7 +1196,7 @@ accept_exchanges (void *cls, GNUNET_CONTAINER_DLL_insert (exchange_head, exchange_tail, exchange); - exchange->pending = GNUNET_YES; + exchange->pending = true; GNUNET_assert (NULL == exchange->retry_task); exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange, exchange); @@ -1228,7 +1236,7 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) { json_t *j_exchange; - if (GNUNET_YES != exchange->trusted) + if (! exchange->trusted) continue; j_exchange = json_pack ("{s:s, s:o}", "url", exchange->url, @@ -1270,6 +1278,7 @@ TMH_EXCHANGES_done () GNUNET_free (af); } GNUNET_free (f->wire_method); + GNUNET_free (f->payto_uri); GNUNET_free (f); } if (NULL != exchange->wire_request) diff --git a/src/backend/taler-merchant-httpd_exchanges.h b/src/backend/taler-merchant-httpd_exchanges.h index 3a47408b..4fcb8125 100644 --- a/src/backend/taler-merchant-httpd_exchanges.h +++ b/src/backend/taler-merchant-httpd_exchanges.h @@ -61,15 +61,17 @@ TMH_EXCHANGES_done (void); * @param cls closure * @param hr HTTP response details * @param eh handle to the exchange context + * @param payto_uri payto://-URI of the exchange * @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 + * @param exchange_trusted true if this exchange is trusted by config */ typedef void (*TMH_EXCHANGES_FindContinuation)(void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, + const char *payto_uri, const struct TALER_Amount *wire_fee, - int exchange_trusted); + bool exchange_trusted); /** diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index f22ac8a3..15a40f23 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -496,18 +496,21 @@ refund_cb (void *cls, * @param cls a `struct CoinRefund *` * @param hr HTTP response details * @param eh handle to the exchange context + * @param payto_uri payto://-URI of the exchange * @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 + * @param exchange_trusted true if this exchange is trusted by config */ static void exchange_found_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, + const char *payto_uri, const struct TALER_Amount *wire_fee, - int exchange_trusted) + bool exchange_trusted) { struct CoinRefund *cr = cls; + (void) payto_uri; cr->fo = NULL; if (TALER_EC_NONE == hr->ec) { diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c index 564f0e3c..19f7223e 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c @@ -469,21 +469,25 @@ refund_cb (void *cls, * * @param cls the `struct AbortContext` * @param hr HTTP response details + * @param payto_uri payto://-URI of the exchange * @param exchange_handle NULL if exchange was not found to be acceptable * @param wire_fee current applicable fee for dealing with @a exchange_handle, * NULL if not available - * @param exchange_trusted #GNUNET_YES if this exchange is + * @param exchange_trusted true if this exchange is * trusted by config */ static void process_abort_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *exchange_handle, + const char *payto_uri, const struct TALER_Amount *wire_fee, - int exchange_trusted) + bool exchange_trusted) { struct AbortContext *ac = cls; + (void) payto_uri; + (void) exchange_trusted; ac->fo = NULL; GNUNET_assert (GNUNET_YES == ac->suspended); if (MHD_HTTP_OK != hr->http_status) diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c index ca519a9f..b1f5c037 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -689,22 +689,25 @@ deposit_cb (void *cls, * @param cls the `struct PayContext` * @param hr HTTP response details * @param exchange_handle NULL if exchange was not found to be acceptable + * @param payto_uri payto://-URI of the exchange * @param wire_fee current applicable fee for dealing with @a exchange_handle, * NULL if not available - * @param exchange_trusted #GNUNET_YES if this exchange is + * @param exchange_trusted true if this exchange is * trusted by config */ static void process_pay_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *exchange_handle, + const char *payto_uri, const struct TALER_Amount *wire_fee, - int exchange_trusted) + bool exchange_trusted) { struct PayContext *pc = cls; struct TMH_HandlerContext *hc = pc->hc; const struct TALER_EXCHANGE_Keys *keys; + (void) payto_uri; pc->fo = NULL; GNUNET_assert (GNUNET_YES == pc->suspended); if (MHD_HTTP_OK != hr->http_status) diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.c b/src/backend/taler-merchant-httpd_private-post-reserves.c new file mode 100644 index 00000000..abb6d0b5 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-reserves.c @@ -0,0 +1,288 @@ +/* + This file is part of TALER + (C) 2020 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_private-post-reserves.c + * @brief implementing POST /reserves request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_exchanges.h" +#include "taler-merchant-httpd_private-post-reserves.h" +#include <taler/taler_json_lib.h> + + +/** + * Information we keep for an individual call to the POST /reserves handler. + */ +struct PostReserveContext +{ + + /** + * Stored in a DLL. + */ + struct PostReserveContext *next; + + /** + * Stored in a DLL. + */ + struct PostReserveContext *prev; + + /** + * Array with @e coins_cnt coins we are despositing. + */ + struct DepositConfirmation *dc; + + /** + * MHD connection to return to + */ + struct MHD_Connection *connection; + + /** + * Details about the client's request. + */ + struct TMH_HandlerContext *hc; + + /** + * URL of the exchange. + */ + const char *exchange_url; + + /** + * URI of the exchange where the payment needs to be made to. + */ + char *payto_uri; + + /** + * Handle for contacting the exchange. + */ + struct TMH_EXCHANGES_FindOperation *fo; + + /** + * Initial balance of the reserve. + */ + struct TALER_Amount initial_balance; + + /** + * When will the reserve expire. + */ + struct GNUNET_TIME_Absolute reserve_expiration; + + /** + * Are we suspended? + */ + bool suspended; +}; + + +/** + * Stored in a DLL. + */ +static struct PostReserveContext *rc_head; + +/** + * Stored in a DLL. + */ +static struct PostReserveContext *rc_tail; + + +/** + * Force all post reserve contexts to be resumed as we are about + * to shut down MHD. + */ +void +TMH_force_rc_resume () +{ + for (struct PostReserveContext *rc = rc_head; + NULL != rc; + rc = rc->next) + { + if (rc->suspended) + { + rc->suspended = false; + MHD_resume_connection (rc->connection); + GNUNET_CONTAINER_DLL_remove (rc_head, + rc_tail, + rc); + } + if (NULL != rc->fo) + { + TMH_EXCHANGES_find_exchange_cancel (rc->fo); + rc->fo = NULL; + } + } +} + + +/** + * Custom cleanup routine for a `struct PostReserveContext`. + * + * @param cls the `struct PostReserveContext` to clean up. + */ +static void +reserve_context_cleanup (void *cls) +{ + struct PostReserveContext *rc = cls; + + if (NULL != rc->fo) + { + TMH_EXCHANGES_find_exchange_cancel (rc->fo); + rc->fo = NULL; + } + GNUNET_assert (! rc->suspended); + GNUNET_free_non_null (rc->payto_uri); + GNUNET_free (rc); +} + + +/** + * Function called with the result of a #TMH_EXCHANGES_find_exchange() + * operation. + * + * @param cls closure with our `struct PostReserveContext *` + * @param hr HTTP response details + * @param payto_uri URI of the exchange for the wire transfer, NULL on errors + * @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 true if this exchange is trusted by config + */ +static void +handle_exchange (void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr, + struct TALER_EXCHANGE_Handle *eh, + const char *payto_uri, + const struct TALER_Amount *wire_fee, + bool exchange_trusted) +{ + struct PostReserveContext *rc = cls; + const struct TALER_EXCHANGE_Keys *keys; + + rc->suspended = false; + MHD_resume_connection (rc->connection); + keys = TALER_EXCHANGE_get_keys (eh); + if ( (NULL != keys) && + (NULL != payto_uri) ) + { + rc->reserve_expiration + = GNUNET_TIME_relative_to_absolute (keys->reserve_closing_delay); + rc->payto_uri = GNUNET_strdup (payto_uri); + } +} + + +/** + * Generate a reserve, given its keys and balance. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_reserves (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct PostReserveContext *rc = hc->ctx; + struct TMH_MerchantInstance *mi = hc->instance; + + GNUNET_assert (NULL != mi); + if (NULL == rc) + { + const char *wire_method; + + rc = GNUNET_new (struct PostReserveContext); + rc->connection = connection; + rc->hc = hc; + hc->ctx = rc; + hc->cc = &reserve_context_cleanup; + + { + enum GNUNET_GenericReturnValue res; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("exchange_url", + &rc->exchange_url), + GNUNET_JSON_spec_string ("wire_method", + &wire_method), + TALER_JSON_spec_amount ("initial_balance", + &rc->initial_balance), + GNUNET_JSON_spec_end () + }; + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + rc->fo = TMH_EXCHANGES_find_exchange (rc->exchange_url, + wire_method, + GNUNET_NO, + &handle_exchange, + rc); + rc->suspended = true; + GNUNET_CONTAINER_DLL_insert (rc_head, + rc_tail, + rc); + MHD_suspend_connection (connection); + return MHD_YES; + } + + GNUNET_assert (! rc->suspended); + if (NULL == rc->payto_uri) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_RESERVES_POST_UNSUPPORTED_WIRE_METHOD, + "Exchange does not support wire method"); + } + { + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_ReservePrivateKeyP reserve_priv; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_CRYPTO_eddsa_key_create (&reserve_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv, + &reserve_pub.eddsa_pub); + qs = TMH_db->insert_reserve (TMH_db->cls, + mi->settings.id, + &reserve_priv, + &reserve_pub, + rc->exchange_url, + &rc->initial_balance, + rc->reserve_expiration); + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + if (qs < 0) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_RESERVES_POST_DB_COMMIT_HARD_ERROR, + "Failed to commit transaction"); + return TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o,s:s}", + "reserve_pub", + GNUNET_JSON_from_data_auto (&reserve_pub), + "payto_uri", + rc->payto_uri); + } +} + + +/* end of taler-merchant-httpd_private-post-reserves.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.h b/src/backend/taler-merchant-httpd_private-post-reserves.h new file mode 100644 index 00000000..2c356488 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-reserves.h @@ -0,0 +1,50 @@ +/* + This file is part of TALER + (C) 2020 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_private-post-reserves.h + * @brief implementing POST /reserves request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_RESERVES_H +#define TALER_MERCHANT_HTTPD_PRIVATE_POST_RESERVES_H +#include "taler-merchant-httpd.h" + +/** + * Force all post reserve contexts to be resumed as we are about + * to shut down MHD. + */ +void +TMH_force_rc_resume (); + + +/** + * Generate a reserve entry in our inventory. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_reserves (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c index 0b9b6728..3687f6da 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.c +++ b/src/backend/taler-merchant-httpd_private-post-transfers.c @@ -534,6 +534,7 @@ retry: * @param cls the `struct PostTransfersContext` * @param hr HTTP response details * @param eh NULL if exchange was not found to be acceptable + * @param payto_uri payto://-URI of the exchange * @param wire_fee NULL (we did not specify a wire method) * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config */ @@ -541,11 +542,14 @@ static void process_transfer_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, + const char *payto_uri, const struct TALER_Amount *wire_fee, - int exchange_trusted) + bool exchange_trusted) { struct PostTransfersContext *ptc = cls; + (void) payto_uri; + (void) exchange_trusted; ptc->fo = NULL; if (MHD_HTTP_OK != hr->http_status) { diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 5321b574..066c2b2d 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -1456,6 +1456,32 @@ struct TALER_MERCHANTDB_Plugin void *cb_cls); + /** + * Add @a credit to a reserve to be used for tipping. Note that + * this function does not actually perform any wire transfers to + * credit the reserve, it merely tells the merchant backend that + * a reserve now exists. This has to happen before tips can be + * authorized. + * + * @param cls closure, typically a connection to the db + * @param instance_id which instance is the reserve tied to + * @param reserve_priv which reserve is topped up or created + * @param reserve_pub which reserve is topped up or created + * @param exchange_url what URL is the exchange reachable at where the reserve is located + * @param initial_balance how much money will be added to the reserve + * @param expiration when does the reserve expire? + * @return transaction status, usually + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success + */ + enum GNUNET_DB_QueryStatus + (*insert_reserve)(void *cls, + const char *instance_id, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_ReservePublicKeyP *reserve_pub, + const char *exchange_url, + const struct TALER_Amount *initial_balance, + struct GNUNET_TIME_Absolute expiration); + /* ****************** OLD API ******************** */ |