aboutsummaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-10-14 18:56:59 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2022-10-14 18:56:59 +0200
commit054e157af8c768062dd0a8e66614da18407fca28 (patch)
treefcd792c6ed7f4384e9b29f8e200f62837cd00f1e /src/exchange
parent956e3c3065ddb762dbe01fd720cc5ef403232564 (diff)
WIP: policy_details handling continued
- policy details generated on deposit/batch-deposit requests - insert or update of policy details in the DB - accumulation of amounts of multiple deposits for the same policy_details
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/taler-exchange-httpd.c141
-rw-r--r--src/exchange/taler-exchange-httpd_batch-deposit.c91
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c100
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c166
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.h15
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
6 files changed, 316 insertions, 200 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 32cbd8dd..1cbaee96 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -1041,138 +1041,6 @@ handle_post_auditors (struct TEH_RequestContext *rc,
root);
}
-
-/**
- * Handle GET "/extensions/..." requests.
- *
- * @param rc request context
- * @param args array of additional options
- * @return MHD result code
- */
-static MHD_RESULT
-handle_get_extensions (struct TEH_RequestContext *rc,
- const char *const args[])
-{
- const struct TALER_Extension *ext = NULL;
-
- if (NULL == args[0])
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION");
- }
-
- ext = TALER_extensions_get_by_name (args[0]);
- if (NULL == ext)
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION unknown");
- }
-
- if (NULL == ext->policy_get_handler)
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_IMPLEMENTED,
- TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
- "GET /extensions/$EXTENSION not supported");
-
- return ext->policy_get_handler (
- rc->connection,
- &args[1]);
-}
-
-
-/* @brief function pointer for TALER_extension.check */
-static enum GNUNET_GenericReturnValue
-check_serial_ids (
- struct GNUNET_HashCode serial_ids[],
- size_t size)
-{
- for (size_t i = 0; i < size; i++)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got to check serial_id: %s\n",
- GNUNET_h2s (&serial_ids[i]));
- return GNUNET_OK;
-}
-
-
-/**
- * Handle POST "/extensions/..." requests.
- *
- * @param rc request context
- * @param root uploaded JSON data
- * @param args array of additional options
- * @return MHD result code
- */
-static MHD_RESULT
-handle_post_extensions (struct TEH_RequestContext *rc,
- const json_t *root,
- const char *const args[])
-{
- const struct TALER_Extension *ext = NULL;
- json_t *output;
-
- if (NULL == args[0])
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION");
- }
-
- ext = TALER_extensions_get_by_name (args[0]);
- if (NULL == ext)
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION unknown");
- }
-
- if (NULL == ext->policy_post_handler)
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_IMPLEMENTED,
- TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
- "POST /extensions/$EXTENSION not supported");
-
- {
- enum GNUNET_GenericReturnValue ret;
- struct TALER_PolicyFulfilmentOutcome *outcome;
-
- ret = ext->policy_post_handler (root,
- &args[1],
- &check_serial_ids,
- &outcome,
- &output);
-
- if (GNUNET_OK != ret)
- {
- TALER_policy_fulfilment_outcome_free (outcome);
- TALER_MHD_reply_json_steal (
- rc->connection,
- output,
- MHD_HTTP_BAD_REQUEST);
- }
-
- /* TODO: deal with outcome */
- {
- for (size_t i = 0; i < outcome->len; i++)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got outcome for serial_id: %s, state: %s, amount: %s\n",
- GNUNET_h2s (&outcome->positions[i].serial_id),
- TALER_policy_fulfilment_state_str (
- outcome->positions[i].state),
- TALER_amount_to_string (&outcome->positions[i].new_amount));
- }
- }
-
- }
-
- return TALER_MHD_reply_json_steal (rc->connection,
- output,
- MHD_HTTP_OK);
-}
-
-
/**
* Handle incoming HTTP request.
*
@@ -1393,15 +1261,8 @@ handle_mhd_request (void *cls,
/* extensions endpoints */
{
.url = "extensions",
- .method = MHD_HTTP_METHOD_GET,
- .handler.get = &handle_get_extensions,
- .nargs = 4, /* Arbitrary upper bound */
- .nargs_is_upper_bound = true,
- },
- {
- .url = "extensions",
.method = MHD_HTTP_METHOD_POST,
- .handler.post = &handle_post_extensions,
+ .handler.post = &TEH_extensions_post_handler,
.nargs = 4, /* Arbitrary upper bound */
.nargs_is_upper_bound = true,
},
diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c
index d4a9666e..14267e2a 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -90,8 +90,12 @@ struct BatchDepositContext
* Additional details for policy relevant for this
* deposit operation, possibly NULL!
*/
- json_t *policy_details;
- bool has_policy_details;
+ json_t *policy_json;
+
+ /**
+ * Will be true if policy_json were provided
+ */
+ bool has_policy;
/**
* Hash over @e policy_details, might be all zero;
@@ -99,6 +103,19 @@ struct BatchDepositContext
struct TALER_ExtensionPolicyHashP h_policy;
/**
+ * If @e policy_json was present, the corresponding policy extension
+ * calculates these details. These will be persisted in the policy_details
+ * table.
+ */
+ struct TALER_PolicyDetails policy_details;
+
+ /**
+ * When @e policy_details are persisted, this contains the id of the record
+ * in the policy_details table.
+ */
+ uint64_t policy_details_serial_id;
+
+ /**
* Time when this request was generated. Used, for example, to
* assess when (roughly) the income was achieved for tax purposes.
* Note that the Exchange will only check that the timestamp is not "too
@@ -174,7 +191,7 @@ again:
&TEH_keys_exchange_sign_,
&bdc->h_contract_terms,
&bdc->h_wire,
- bdc->has_policy_details ? &bdc->h_policy : NULL,
+ bdc->has_policy ? &bdc->h_policy : NULL,
bdc->exchange_timestamp,
bdc->wire_deadline,
bdc->refund_deadline,
@@ -247,6 +264,21 @@ batch_deposit_transaction (void *cls,
bool balance_ok;
bool in_conflict;
+
+ /* If the deposit has a policy associated to it, persist it. This will
+ * insert or update the record. */
+ if (dc->has_policy)
+ {
+ qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
+ &dc->policy_details,
+ &dc->policy_details_serial_id,
+ &dc->policy_details.accumulated_total,
+ &dc->policy_details.fulfillment_state);
+ if (qs < 0)
+ return qs;
+ }
+
+ /* deposit the individutal coins */
for (unsigned int i = 0; i<dc->num_coins; i++)
{
const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i];
@@ -258,13 +290,19 @@ batch_deposit_transaction (void *cls,
mhd_ret);
if (qs < 0)
return qs;
- qs = TEH_plugin->do_deposit (TEH_plugin->cls,
- deposit,
- known_coin_id,
- &dc->h_payto,
- &dc->exchange_timestamp,
- &balance_ok,
- &in_conflict);
+
+ qs = TEH_plugin->do_deposit (
+ TEH_plugin->cls,
+ deposit,
+ known_coin_id,
+ &dc->h_payto,
+ (dc->has_policy)
+ ? &dc->policy_details_serial_id
+ : NULL,
+ &dc->exchange_timestamp,
+ &balance_ok,
+ &in_conflict);
+
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -474,7 +512,7 @@ parse_coin (struct MHD_Connection *connection,
&dc->h_wire,
&dc->h_contract_terms,
&deposit->coin.h_age_commitment,
- dc->has_policy_details ? &dc->h_policy :
+ dc->has_policy ? &dc->h_policy :
NULL,
&deposit->coin.denom_pub_hash,
dc->timestamp,
@@ -501,7 +539,7 @@ parse_coin (struct MHD_Connection *connection,
but rather insert them ONCE and then per-coin only use
the resulting extension UUID/serial; so the data structure
here should be changed once we look at extensions in earnest. */
- deposit->policy_details = dc->policy_details;
+ deposit->policy_json = dc->policy_json;
deposit->timestamp = dc->timestamp;
deposit->refund_deadline = dc->refund_deadline;
deposit->wire_deadline = dc->wire_deadline;
@@ -518,7 +556,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
struct BatchDepositContext dc;
json_t *coins;
bool no_refund_deadline = true;
- bool no_policy_details = true;
+ bool no_policy_json = true;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&dc.payto_uri),
@@ -532,8 +570,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
&coins),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("policy",
- &dc.policy_details),
- &no_policy_details),
+ &dc.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_timestamp ("timestamp",
&dc.timestamp),
GNUNET_JSON_spec_mark_optional (
@@ -564,7 +602,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
return MHD_YES; /* failure */
}
- dc.has_policy_details = ! no_policy_details;
+ dc.has_policy = ! no_policy_json;
/* validate merchant's wire details (as far as we can) */
{
@@ -610,11 +648,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
TALER_merchant_wire_signature_hash (dc.payto_uri,
&dc.wire_salt,
&dc.h_wire);
- if (dc.has_policy_details)
+
+ /* handle policy, if present */
+ if (dc.has_policy)
{
- TALER_deposit_policy_hash (dc.policy_details,
+ const char *error_hint = NULL;
+
+ if (GNUNET_OK !=
+ TALER_extensions_create_policy_details (
+ dc.policy_json,
+ &dc.policy_details,
+ &error_hint))
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+ error_hint);
+
+ TALER_deposit_policy_hash (dc.policy_json,
&dc.h_policy);
}
+
dc.num_coins = json_array_size (coins);
if (0 == dc.num_coins)
{
@@ -651,6 +704,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
GNUNET_JSON_parse_free (spec);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
+
+ /* FIXME: sum all contributions for the policy_details.accumulated_total */
}
dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 12741c6b..f44775c0 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -132,6 +132,12 @@ struct DepositContext
*/
uint64_t known_coin_id;
+ /**
+ * When deposit->has_policy is true, and deposit->policy_details are
+ * persisted, this contains the id of the record in the policy_details table.
+ */
+ uint64_t policy_details_serial_id;
+
};
@@ -165,14 +171,36 @@ deposit_transaction (void *cls,
if (qs < 0)
return qs;
+ /* If the deposit has a policy associated to it, persist it. This will
+ * insert or update the record. */
+ if (dc->deposit->has_policy)
+ {
+ struct TALER_Amount accumulated_total;
+ enum TALER_PolicyFulfillmentState fulfillment_state;
+ qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
+ &dc->deposit->policy_details,
+ &dc->policy_details_serial_id,
+ &accumulated_total,
+ &fulfillment_state);
+
+ /* FIXME: what to do with accumulated_total and fulfillment_state ? */
+
+ if (qs < 0)
+ return qs;
+ }
+
+ qs = TEH_plugin->do_deposit (
+ TEH_plugin->cls,
+ dc->deposit,
+ dc->known_coin_id,
+ &dc->h_payto,
+ (dc->deposit->has_policy)
+ ? &dc->policy_details_serial_id
+ : NULL,
+ &dc->exchange_timestamp,
+ &balance_ok,
+ &in_conflict);
- qs = TEH_plugin->do_deposit (TEH_plugin->cls,
- dc->deposit,
- dc->known_coin_id,
- &dc->h_payto,
- &dc->exchange_timestamp,
- &balance_ok,
- &in_conflict);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -219,7 +247,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Deposit deposit;
const char *payto_uri;
struct TALER_ExtensionPolicyHashP *ph_policy = NULL;
- bool no_policy_details;
+ bool no_policy_json;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&payto_uri),
@@ -255,8 +283,8 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.wire_deadline),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("policy",
- &deposit.policy_details),
- &no_policy_details),
+ &deposit.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_end ()
};
struct TALER_MerchantWireHashP h_wire;
@@ -284,7 +312,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
}
/* set the state of the policy presence in the deposit struct */
- deposit.has_policy_details = ! no_policy_details;
+ deposit.has_policy = ! no_policy_json;
/* validate merchant's wire details (as far as we can) */
{
@@ -333,28 +361,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&h_wire);
dc.deposit = &deposit;
- /* Check policy */
- if (deposit.has_policy_details)
- {
- const char *error_hint = NULL;
- enum TALER_PolicyFulfilmentState state_on_timeout;
-
- if (GNUNET_OK !=
- TALER_extensions_extract_meta_data_from_policy_details (
- deposit.policy_details,
- &deposit.policy_serial_id,
- &deposit.policy_deadline,
- &state_on_timeout,
- &error_hint))
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
- error_hint);
-
- GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state_on_timeout);
- deposit.policy_state_on_timeout = state_on_timeout;
- }
-
/* new deposit */
dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
/* check denomination exists and is valid */
@@ -420,13 +426,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
- if (deposit.has_policy_details)
- {
- TALER_deposit_policy_hash (deposit.policy_details,
- &deposit.h_policy);
- ph_policy = &deposit.h_policy;
- }
-
deposit.deposit_fee = dk->meta.fees.deposit;
/* check coin signature */
switch (dk->denom_pub.cipher)
@@ -463,6 +462,27 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
+ /* Check policy input and create policy details */
+ if (deposit.has_policy)
+ {
+ const char *error_hint = NULL;
+
+ if (GNUNET_OK !=
+ TALER_extensions_create_policy_details (
+ deposit.policy_json,
+ &deposit.policy_details,
+ &error_hint))
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+ error_hint);
+
+ TALER_deposit_policy_hash (deposit.policy_json,
+ &deposit.h_policy);
+ ph_policy = &deposit.h_policy;
+ }
+
+
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_wallet_deposit_verify (&deposit.amount_with_fee,
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index e8d3e4af..8e8f0e01 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -14,7 +14,7 @@
*/
/**
* @file taler-exchange-httpd_extensions.c
- * @brief Handle extensions (age-restriction, peer2peer)
+ * @brief Handle extensions (age-restriction, policy extensions)
* @author Özgür Kesim
*/
#include "platform.h"
@@ -232,4 +232,168 @@ TEH_extensions_done ()
}
+/*
+ * @brief Execute database transactions for /extensions/policy_* POST requests.
+ *
+ * @param cls a `struct TALER_PolicyFulfillmentOutcome`
+ * @param connection MHD request context
+ * @param[out] mhd_ret set to MHD status on error
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+policy_fulfillment_transaction (
+ void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls;
+
+ return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls,
+ fulfillment);
+}
+
+
+MHD_RESULT
+TEH_extensions_post_handler (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[])
+{
+ const struct TALER_Extension *ext = NULL;
+ json_t *output;
+ struct TALER_PolicyDetails *policy_details = NULL;
+ size_t policy_details_count = 0;
+
+
+ if (NULL == args[0])
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "/extensions/$EXTENSION");
+ }
+
+ ext = TALER_extensions_get_by_name (args[0]);
+ if (NULL == ext)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "/extensions/$EXTENSION unknown");
+ }
+
+ if (NULL == ext->policy_post_handler)
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "POST /extensions/$EXTENSION not supported");
+
+ /* Extract hash_codes and retrieve related policy_details from the DB */
+ {
+ enum GNUNET_GenericReturnValue ret;
+ enum GNUNET_DB_QueryStatus qs;
+ const char *error_msg;
+ struct GNUNET_HashCode *hcs;
+ size_t len;
+ json_t*val;
+ size_t idx;
+ json_t *jhash_codes = json_object_get (root,
+ "policy_hash_codes");
+ if (! json_is_array (jhash_codes))
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "policy_hash_codes are missing");
+
+ len = json_array_size (jhash_codes);
+ hcs = GNUNET_new_array (len,
+ struct GNUNET_HashCode);
+ policy_details = GNUNET_new_array (len,
+ struct TALER_PolicyDetails);
+
+ json_array_foreach (jhash_codes, idx, val)
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = GNUNET_JSON_parse (val,
+ spec,
+ &error_msg,
+ NULL);
+ if (GNUNET_OK != ret)
+ break;
+
+ qs = TEH_plugin->get_policy_details (TEH_plugin->cls,
+ &hcs[idx],
+ &policy_details[idx]);
+ if (qs < 0)
+ {
+ error_msg = "a policy_hash_code couldn't be found";
+ break;
+ }
+ }
+
+ GNUNET_free (hcs);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_free (policy_details);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ error_msg);
+ }
+ }
+
+
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = ext->policy_post_handler (root,
+ &args[1],
+ policy_details,
+ policy_details_count,
+ &output);
+
+ if (GNUNET_OK != ret)
+ {
+ TALER_MHD_reply_json_steal (
+ rc->connection,
+ output,
+ MHD_HTTP_BAD_REQUEST);
+ }
+
+ /* execute fulfillment transaction */
+ {
+ MHD_RESULT mhd_ret;
+ struct TALER_PolicyFulfillmentTransactionData fulfillment = {
+ .proof = root,
+ .timestamp = GNUNET_TIME_timestamp_get (),
+ .details = policy_details,
+ .details_count = policy_details_count
+ };
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "execute policy fulfillment",
+ TEH_MT_REQUEST_POLICY_FULFILMENT,
+ &mhd_ret,
+ &policy_fulfillment_transaction,
+ &fulfillment))
+ {
+ json_decref (output);
+ return mhd_ret;
+ }
+ }
+ }
+
+ return TALER_MHD_reply_json_steal (rc->connection,
+ output,
+ MHD_HTTP_OK);
+}
+
+
/* end of taler-exchange-httpd_extensions.c */
diff --git a/src/exchange/taler-exchange-httpd_extensions.h b/src/exchange/taler-exchange-httpd_extensions.h
index 4659b653..e435f8f0 100644
--- a/src/exchange/taler-exchange-httpd_extensions.h
+++ b/src/exchange/taler-exchange-httpd_extensions.h
@@ -40,4 +40,19 @@ TEH_extensions_init (void);
void
TEH_extensions_done (void);
+
+/**
+ * Handle POST "/extensions/..." requests.
+ *
+ * @param rc request context
+ * @param root uploaded JSON data
+ * @param args array of additional options
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_extensions_post_handler (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[]);
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h
index c1b7326f..5acd3605 100644
--- a/src/exchange/taler-exchange-httpd_metrics.h
+++ b/src/exchange/taler-exchange-httpd_metrics.h
@@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest
TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
- TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */
+ TEH_MT_REQUEST_POLICY_FULFILMENT = 13,
+ TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
};
/**