summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-05-18 19:11:08 +0200
committerChristian Grothoff <christian@grothoff.org>2020-05-18 19:11:08 +0200
commit49bb51daf5486817c3adc8a63137de1ec9d98589 (patch)
treec1b3151faf42ab2abdb64b46b4e10c0330183b84
parent1792cbaa740194e7aebb051f7ce49b3d17976648 (diff)
downloadmerchant-49bb51daf5486817c3adc8a63137de1ec9d98589.tar.gz
merchant-49bb51daf5486817c3adc8a63137de1ec9d98589.tar.bz2
merchant-49bb51daf5486817c3adc8a63137de1ec9d98589.zip
implement POST /reserves
-rw-r--r--src/backend/Makefile.am2
-rw-r--r--src/backend/taler-merchant-httpd.c11
-rw-r--r--src/backend/taler-merchant-httpd_auditors.c6
-rw-r--r--src/backend/taler-merchant-httpd_auditors.h4
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c111
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.h6
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.c7
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-abort.c8
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c7
-rw-r--r--src/backend/taler-merchant-httpd_private-post-reserves.c288
-rw-r--r--src/backend/taler-merchant-httpd_private-post-reserves.h50
-rw-r--r--src/backend/taler-merchant-httpd_private-post-transfers.c6
-rw-r--r--src/include/taler_merchantdb_plugin.h26
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 ******************** */