summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-18 13:34:48 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-18 13:34:48 +0200
commit741f7127849d8e5c8e5feacc34fc16d415fc2944 (patch)
treeb15b02519eca43db7f8742e9bf68ee2d02f73375
parentb03f6cf5044193931b028e867642f13ee9a24f3b (diff)
downloadmerchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.tar.gz
merchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.tar.bz2
merchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.zip
implement POST /instances
-rw-r--r--src/backend/Makefile.am3
-rw-r--r--src/backend/taler-merchant-httpd.c41
-rw-r--r--src/backend/taler-merchant-httpd.h27
-rw-r--r--src/backend/taler-merchant-httpd_private-get-instances.c8
-rw-r--r--src/backend/taler-merchant-httpd_private-post-instances.c411
-rw-r--r--src/backend/taler-merchant-httpd_private-post-instances.h43
-rw-r--r--src/backenddb/merchant-0001.sql10
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c131
-rw-r--r--src/include/taler_merchantdb_plugin.h36
9 files changed, 662 insertions, 48 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 1f98de35..db8969de 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -23,7 +23,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_config.c taler-merchant-httpd_config.h \
taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
- taler-merchant-httpd_private-get-instances.c taler-merchant-httpd_private-get-instances.h
+ taler-merchant-httpd_private-get-instances.c taler-merchant-httpd_private-get-instances.h \
+ taler-merchant-httpd_private-post-instances.c taler-merchant-httpd_private-post-instances.h
DEAD = \
taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 7ad0f85a..7f5e32f1 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -29,6 +29,7 @@
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_private-get-instances.h"
+#include "taler-merchant-httpd_private-post-instances.h"
/**
* Backlog for listen operation on unix-domain sockets.
@@ -131,7 +132,7 @@ TMH_instance_decref (struct TMH_MerchantInstance *mi)
GNUNET_free (mi->settings.id);
GNUNET_free (mi->settings.name);
- json_decref (mi->settings.location);
+ json_decref (mi->settings.address);
json_decref (mi->settings.jurisdiction);
GNUNET_free (mi);
}
@@ -269,7 +270,7 @@ TMH_long_poll_suspend (const char *order_id,
const struct TALER_Amount *min_refund)
{
compute_pay_key (order_id,
- &mi->pubkey,
+ &mi->merchant_pub,
&sc->key);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Suspending operation on key %s\n",
@@ -358,7 +359,7 @@ TMH_long_poll_resume (const char *order_id,
struct GNUNET_HashCode key;
compute_pay_key (order_id,
- &mi->pubkey,
+ &mi->merchant_pub,
&key);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming operations suspended pending payment on key %s\n",
@@ -480,8 +481,8 @@ handle_mhd_completion_callback (void *cls,
hc->cc (hc->ctx);
TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
GNUNET_free_non_null (hc->infix);
- if (NULL != hc->json)
- json_decref (hc->json);
+ if (NULL != hc->request_body)
+ json_decref (hc->request_body);
GNUNET_free (hc);
*con_cls = NULL;
}
@@ -600,8 +601,8 @@ prepare_daemon (void)
* @param instance_id identifier of the instance to resolve
* @return NULL if that instance is unknown to us
*/
-static struct TMH_MerchantInstance *
-lookup_instance (const char *instance_id)
+struct TMH_MerchantInstance *
+TMH_lookup_instance (const char *instance_id)
{
struct GNUNET_HashCode h_instance;
@@ -634,7 +635,7 @@ TMH_add_instance (struct TMH_MerchantInstance *mi)
const char *id;
int ret;
- id = mi->id;
+ id = mi->settings.id;
if (NULL == id)
id = "default";
GNUNET_CRYPTO_hash (id,
@@ -728,6 +729,12 @@ url_handler (void *cls,
.handler = &TMH_private_get_instances
},
{
+ .url_prefix = "/instances",
+ .method = MHD_HTTP_METHOD_POST,
+ .skip_instance = true,
+ .handler = &TMH_private_post_instances
+ },
+ {
NULL
}
};
@@ -778,7 +785,7 @@ url_handler (void *cls,
GNUNET_assert (NULL != hc->rh);
GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
if ( (hc->has_body) &&
- (NULL == hc->json) )
+ (NULL == hc->request_body) )
{
int res;
@@ -786,13 +793,13 @@ url_handler (void *cls,
&hc->json_parse_context,
upload_data,
upload_data_size,
- &hc->json);
+ &hc->request_body);
if (GNUNET_SYSERR == res)
return MHD_NO;
/* A error response was already generated */
if ( (GNUNET_NO == res) ||
/* or, need more data to accomplish parsing */
- (NULL == hc->json) )
+ (NULL == hc->request_body) )
return MHD_YES;
}
return hc->rh->handler (hc->rh,
@@ -857,14 +864,14 @@ url_handler (void *cls,
}
instance_id = GNUNET_strndup (istart,
slash - istart);
- hc->instance = lookup_instance (instance_id);
+ hc->instance = TMH_lookup_instance (instance_id);
GNUNET_free (instance_id);
url = slash;
}
else
{
/* use 'default' */
- hc->instance = lookup_instance (NULL);
+ hc->instance = TMH_lookup_instance (NULL);
}
}
@@ -993,7 +1000,7 @@ url_handler (void *cls,
and refuse if it is too big? (Note: maximum upload
size may need to vary based on the handler.) */
- GNUNET_break (NULL == hc->json); /* can't have it already */
+ GNUNET_break (NULL == hc->request_body); /* can't have it already */
return MHD_YES; /* proceed with upload */
}
return hc->rh->handler (hc->rh,
@@ -1029,10 +1036,10 @@ add_instance_cb (void *cls,
mi->settings = *is;
mi->settings.id = GNUNET_strdup (mi->settings.id);
mi->settings.name = GNUNET_strdup (mi->settings.name);
- mi->settings.location = json_incref (mi->settings.location);
+ mi->settings.address = json_incref (mi->settings.address);
mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
- mi->privkey = *merchant_priv;
- mi->pubkey = *merchant_pub;
+ mi->merchant_priv = *merchant_priv;
+ mi->merchant_pub = *merchant_pub;
for (unsigned int i = 0; i<accounts_length; i++)
{
const struct TALER_MERCHANTDB_AccountDetails *acc = &accounts[i];
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 1d5715d9..3a8b3072 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -82,18 +82,6 @@ struct TMH_MerchantInstance
{
/**
- * Instance's mnemonic identifier. This value lives as long as
- * the configuration is kept in memory, as it's as substring of
- * a section name
- */
- char *id;
-
- /**
- * Legal name of the merchant.
- */
- char *name;
-
- /**
* Next entry in DLL.
*/
struct TMH_WireMethod *wm_head;
@@ -106,12 +94,12 @@ struct TMH_MerchantInstance
/**
* Merchant's private key.
*/
- struct TALER_MerchantPrivateKeyP privkey;
+ struct TALER_MerchantPrivateKeyP merchant_priv;
/**
* Merchant's public key
*/
- struct TALER_MerchantPublicKeyP pubkey;
+ struct TALER_MerchantPublicKeyP merchant_pub;
/**
* General settings for an instance.
@@ -287,7 +275,7 @@ struct TMH_HandlerContext
/**
* JSON body that was uploaded, NULL if @e has_body is false.
*/
- json_t *json;
+ json_t *request_body;
/**
* Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
@@ -425,5 +413,14 @@ TMH_instance_decref (struct TMH_MerchantInstance *mi);
int
TMH_add_instance (struct TMH_MerchantInstance *mi);
+/**
+ * Lookup a merchant instance by its instance ID.
+ *
+ * @param instance_id identifier of the instance to resolve
+ * @return NULL if that instance is unknown to us
+ */
+struct TMH_MerchantInstance *
+TMH_lookup_instance (const char *instance_id);
+
#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-instances.c b/src/backend/taler-merchant-httpd_private-get-instances.c
index bd36817f..74dbfefb 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances.c
@@ -74,11 +74,11 @@ add_instance (void *cls,
json_pack (
"{s:s, s:s, s:o, s:o}",
"name",
- mi->name,
- "instance",
- mi->id,
+ mi->settings.name,
+ "id",
+ mi->settings.id,
"merchant_pub",
- GNUNET_JSON_from_data_auto (&mi->pubkey),
+ GNUNET_JSON_from_data_auto (&mi->merchant_pub),
"payment_targets",
pta)));
return GNUNET_OK;
diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c b/src/backend/taler-merchant-httpd_private-post-instances.c
new file mode 100644
index 00000000..6bbd882b
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-instances.c
@@ -0,0 +1,411 @@
+/*
+ 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-instances.c
+ * @brief implementing POST /instances request handling
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-post-instances.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * How often do we retry the simple INSERT database transaction?
+ */
+#define MAX_RETRIES 3
+
+
+/**
+ * Check if the array of @a payto_uris contains exactly the same
+ * URIs as those already in @a mi (possibly in a different order).
+ *
+ * @param mi a merchant instance with accounts
+ * @param payto_uris a JSON array with accounts (presumably)
+ * @return true if they are 'equal', false if not or of payto_uris is not an array
+ */
+static bool
+accounts_equal (const struct TMH_MerchantInstance *mi,
+ json_t *payto_uris)
+{
+ if (! json_is_array (payto_uris))
+ return false;
+ {
+ unsigned int len = json_array_size (payto_uris);
+ bool matches[GNUNET_NZL (len)];
+ struct TMH_WireMethod *wm;
+
+ memset (matches,
+ 0,
+ sizeof (matches));
+ for (wm = mi->wm_head;
+ NULL != wm;
+ wm = wm->next)
+ {
+ const char *uri = json_string_value (json_object_get (wm->j_wire,
+ "payto_uri"));
+
+ GNUNET_assert (NULL != uri);
+ for (unsigned int i = 0; i<len; i++)
+ {
+ const char *str = json_string_value (json_array_get (payto_uris,
+ i));
+ if (NULL == str)
+ return false;
+ if ( (strcasecmp (uri,
+ str)) )
+ {
+ if (matches[i])
+ {
+ GNUNET_break (0);
+ return false; /* duplicate entry!? */
+ }
+ matches[i] = true;
+ break;
+ }
+ }
+ }
+ for (unsigned int i = 0; i<len; i++)
+ if (! matches[i])
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Free memory used by @a wm
+ *
+ * @param wm wire method to free
+ */
+static void
+free_wm (struct TMH_WireMethod *wm)
+{
+ json_decref (wm->j_wire);
+ GNUNET_free (wm->wire_method);
+ GNUNET_free (wm);
+}
+
+
+/**
+ * Free memory used by @a mi.
+ *
+ * @param mi instance to free
+ */
+static void
+free_mi (struct TMH_MerchantInstance *mi)
+{
+ struct TMH_WireMethod *wm;
+
+ while (NULL != (wm = mi->wm_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (mi->wm_head,
+ mi->wm_tail,
+ wm);
+ free_wm (wm);
+ }
+ GNUNET_free (mi->settings.id);
+ GNUNET_free (mi->settings.name);
+ GNUNET_free (mi);
+}
+
+
+/**
+ * Generate an instance, given its configuration.
+ *
+ * @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_instances (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct TALER_MERCHANTDB_InstanceSettings is;
+ json_t *payto_uris;
+ const char *id;
+ const char *name;
+ struct TMH_WireMethod *wm_head = NULL;
+ struct TMH_WireMethod *wm_tail = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("payto_uris",
+ &payto_uris),
+ GNUNET_JSON_spec_string ("instance",
+ &id),
+ GNUNET_JSON_spec_string ("name",
+ &name),
+ GNUNET_JSON_spec_json ("address",
+ &is.address),
+ GNUNET_JSON_spec_json ("jurisdiction",
+ &is.jurisdiction),
+ TALER_JSON_spec_amount ("default_max_deposit_fee",
+ &is.default_max_deposit_fee),
+ TALER_JSON_spec_amount ("default_max_wire_fee",
+ &is.default_max_wire_fee),
+ GNUNET_JSON_spec_uint32 ("default_wire_fee_amortization",
+ &is.default_wire_fee_amortization),
+ GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
+ &is.default_wire_transfer_delay),
+ GNUNET_JSON_spec_relative_time ("default_pay_delay",
+ &is.default_pay_delay),
+ GNUNET_JSON_spec_end ()
+ };
+
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec);
+ /* json is malformed */
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ return MHD_YES;
+ }
+ /* other internal errors might have occurred */
+ if (GNUNET_SYSERR == res)
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE,
+ "Impossible to parse the order");
+ }
+
+ {
+ /* Test if an instance of this id is known */
+ struct TMH_MerchantInstance *mi;
+
+ mi = TMH_lookup_instance (is.id);
+ if (NULL != mi)
+ {
+ /* Check for idempotency */
+ if ( (0 == strcmp (mi->settings.id,
+ id)) &&
+ (0 == strcmp (mi->settings.name,
+ name)) &&
+ (1 == json_equal (mi->settings.address,
+ is.address)) &&
+ (1 == json_equal (mi->settings.jurisdiction,
+ is.jurisdiction)) &&
+ (0 == TALER_amount_cmp_currency (
+ &mi->settings.default_max_deposit_fee,
+ &is.default_max_deposit_fee)) &&
+ (0 == TALER_amount_cmp (&mi->settings.default_max_deposit_fee,
+ &is.default_max_deposit_fee)) &&
+ (0 == TALER_amount_cmp_currency (&mi->settings.default_max_wire_fee,
+ &is.default_max_wire_fee)) &&
+ (0 == TALER_amount_cmp (&mi->settings.default_max_wire_fee,
+ &is.default_max_wire_fee)) &&
+ (mi->settings.default_wire_fee_amortization ==
+ is.default_wire_fee_amortization) &&
+ (mi->settings.default_wire_transfer_delay.rel_value_us ==
+ is.default_wire_transfer_delay.rel_value_us) &&
+ (mi->settings.default_pay_delay.rel_value_us ==
+ is.default_pay_delay.rel_value_us) &&
+ (accounts_equal (mi,
+ payto_uris)) )
+ {
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ else
+ {
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_POST_INSTANCES_ALREADY_EXISTS,
+ "An instance using this identifier already exists");
+ }
+ }
+ }
+
+ {
+ bool payto_ok = true;
+ unsigned int len;
+
+ if (! json_is_array (payto_uris))
+ {
+ payto_ok = false;
+ len = 0;
+ }
+ else
+ {
+ len = json_array_size (payto_uris);
+ }
+ for (unsigned int i = 0; i<len; i++)
+ {
+ json_t *payto_uri = json_array_get (payto_uris,
+ i);
+
+ if (! json_is_string (payto_uri))
+ {
+ payto_ok = false;
+ break;
+ }
+ /* Test for the same payto:// URI being given twice */
+ for (unsigned int j = 0; j<i; j++)
+ {
+ json_t *old_uri = json_array_get (payto_uris,
+ j);
+ if (json_equal (payto_uri,
+ old_uri))
+ {
+ payto_ok = false;
+ break;
+ }
+ }
+ if (! payto_ok)
+ break;
+
+ {
+ struct TMH_WireMethod *wm;
+ struct GNUNET_HashCode salt;
+
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &salt,
+ sizeof (salt));
+ wm = GNUNET_new (struct TMH_WireMethod);
+ wm->j_wire = json_pack ("{s:O, s:s}",
+ "payto_uri", payto_uri,
+ "salt", GNUNET_JSON_from_data_auto (&salt));
+ GNUNET_assert (NULL != wm->j_wire);
+ /* This also tests for things like the IBAN being malformed */
+ if (GNUNET_OK !=
+ TALER_JSON_merchant_wire_signature_hash (wm->j_wire,
+ &wm->h_wire))
+ {
+ payto_ok = false;
+ GNUNET_free (wm);
+ break;
+ }
+ wm->wire_method
+ = TALER_payto_get_method (json_string_value (payto_uri));
+ GNUNET_assert (NULL != wm->wire_method);
+ wm->active = true;
+ GNUNET_CONTAINER_DLL_insert (wm_head,
+ wm_tail,
+ wm);
+ }
+ }
+ if (! payto_ok)
+ {
+ struct TMH_WireMethod *wm;
+
+ while (NULL != (wm = wm_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (wm_head,
+ wm_tail,
+ wm);
+ free_wm (wm);
+ }
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_POST_INSTANCES_BAD_PAYTO_URIS,
+ "Invalid bank account information");
+ }
+ }
+
+ {
+ struct TMH_MerchantInstance *mi;
+ enum GNUNET_DB_QueryStatus qs;
+
+ mi = GNUNET_new (struct TMH_MerchantInstance);
+ mi->wm_head = wm_head;
+ mi->wm_tail = wm_tail;
+ mi->settings = is;
+ mi->settings.id = GNUNET_strdup (id);
+ mi->settings.name = GNUNET_strdup (name);
+ GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv);
+ GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv,
+ &mi->merchant_pub.eddsa_pub);
+
+ for (unsigned int i = 0; i<MAX_RETRIES; i++)
+ {
+ if (GNUNET_OK !=
+ TMH_db->start (TMH_db->cls,
+ "post /instances"))
+ {
+ GNUNET_JSON_parse_free (spec);
+ free_mi (mi);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_POST_INSTANCES_DB_START_ERROR,
+ "failed to start database transaction");
+ }
+ qs = TMH_db->insert_instance (TMH_db->cls,
+ &mi->merchant_pub,
+ &mi->merchant_priv,
+ &mi->settings);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ continue;
+ }
+ for (struct TMH_WireMethod *wm = wm_head;
+ NULL != wm;
+ wm = wm->next)
+ {
+ struct TALER_MERCHANTDB_AccountDetails ad;
+
+ qs = TMH_db->insert_account (TMH_db->cls,
+ mi->settings.id,
+ &ad);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ break;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ continue;
+ }
+ qs = TMH_db->commit (TMH_db->cls);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ break; /* success! */
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_JSON_parse_free (spec);
+ free_mi (mi);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_POST_INSTANCES_DB_COMMIT_ERROR,
+ "failed to add instance to database");
+ }
+ /* Finally, also update our running process */
+ GNUNET_assert (GNUNET_OK ==
+ TMH_add_instance (mi));
+ }
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+}
+
+
+/* end of taler-merchant-httpd_private-post-instances.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-instances.h b/src/backend/taler-merchant-httpd_private-post-instances.h
new file mode 100644
index 00000000..b30d0396
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-instances.h
@@ -0,0 +1,43 @@
+/*
+ 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-instances.h
+ * @brief implementing POST /instances request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_INSTANCES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_POST_INSTANCES_H
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Generate an instance, given its configuration.
+ *
+ * @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_instances (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 244d0f74..104ea47c 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS merchant_instances
,merchant_pub BYTEA NOT NULL UNIQUE CHECK (LENGTH(merchant_pub)=32)
,merchant_id VARCHAR NOT NULL
,merchant_name VARCHAR NOT NULL
- ,location BYTEA NOT NULL
+ ,address BYTEA NOT NULL
,jurisdiction BYTEA NOT NULL
,default_max_deposit_fee_val INT8 NOT NULL
,default_max_deposit_fee_frac INT4 NOT NULL
@@ -79,8 +79,8 @@ COMMENT ON COLUMN merchant_instances.merchant_id
IS 'identifier of the merchant as used in the base URL (required)';
COMMENT ON COLUMN merchant_instances.merchant_name
IS 'legal name of the merchant as a simple string (required)';
-COMMENT ON COLUMN merchant_instances.location
- IS 'physical location of the merchant as a Location in JSON format (required)';
+COMMENT ON COLUMN merchant_instances.address
+ IS 'physical address of the merchant as a Location in JSON format (required)';
COMMENT ON COLUMN merchant_instances.jurisdiction
IS 'jurisdiction of the merchant as a Location in JSON format (required)';
@@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS merchant_inventory
,total_stock BIGINT NOT NULL
,total_sold BIGINT NOT NULL
,total_lost BIGINT NOT NULL
- ,location BYTEA NOT NULL
+ ,address BYTEA NOT NULL
,next_restock INT8 NOT NULL
,UNIQUE (merchant_serial, product_id)
);
@@ -155,7 +155,7 @@ COMMENT ON COLUMN merchant_inventory.total_sold
IS 'Number of products sold, must be below total_stock, non-negative, may never be lowered';
COMMENT ON COLUMN merchant_inventory.total_lost
IS 'Number of products that used to be in stock but were lost (spoiled, damaged), may never be lowered';
-COMMENT ON COLUMN merchant_inventory.location
+COMMENT ON COLUMN merchant_inventory.address
IS 'JSON formatted Location of where the product is stocked';
COMMENT ON COLUMN merchant_inventory.next_restock
IS 'GNUnet absolute time indicating when the next restock is expected. 0 for unknown.';
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index c0813320..f9cdcad5 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -436,8 +436,8 @@ lookup_instances_cb (void *cls,
&lic->is.name),
GNUNET_PQ_result_spec_string ("id",
&lic->is.id),
- TALER_PQ_result_spec_json ("location",
- &lic->is.location),
+ TALER_PQ_result_spec_json ("address",
+ &lic->is.address),
TALER_PQ_result_spec_json ("jurisdiction",
&lic->is.jurisdiction),
TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_deposit_fee",
@@ -524,6 +524,88 @@ postgres_lookup_instances (void *cls,
}
+/**
+ * Insert information about an instance into our database.
+ *
+ * @param cls closure
+ * @param merchant_pub public key of the instance
+ * @param merchant_priv private key of the instance
+ * @param is details about the instance
+ * @return database result code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_insert_instance (void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const struct TALER_MERCHANTDB_InstanceSettings *is)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_string (is->id),
+ GNUNET_PQ_query_param_string (is->name),
+ TALER_PQ_query_param_json (is->address),
+ TALER_PQ_query_param_json (is->jurisdiction),
+ TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
+ TALER_PQ_query_param_amount (&is->default_max_wire_fee),
+ GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
+ GNUNET_PQ_query_param_relative_time (
+ &is->default_wire_transfer_delay),
+ GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_QueryParam params_priv[] = {
+ GNUNET_PQ_query_param_auto_from_type (merchant_priv),
+ GNUNET_PQ_query_param_string (is->id)
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ check_connection (pg);
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_instance",
+ params);
+ if (qs <= 0)
+ return qs;
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_keys",
+ params_priv);
+}
+
+
+/**
+ * Insert information about an instance's account into our database.
+ *
+ * @param cls closure
+ * @param id identifier of the instance
+ * @param account_details details about the account
+ * @return database result code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_insert_account (
+ void *cls,
+ const char *id,
+ const struct
+ TALER_MERCHANTDB_AccountDetails *account_details)
+{
+ struct PostgresClosure *pg = cls;
+ uint8_t active = account_details->active;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (id),
+ GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
+ GNUNET_PQ_query_param_auto_from_type (&account_details->salt),
+ GNUNET_PQ_query_param_string (account_details->payto_uri),
+ GNUNET_PQ_query_param_auto_from_type (&active),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_account",
+ params);
+
+}
+
+
/* ********************* OLD API ************************** */
/**
@@ -848,7 +930,7 @@ postgres_insert_session_info (void *cls,
* Retrieve the order ID that was used to pay for a resource within a session.
*
* @param cls closure
- * @param[out] order_id location to store the order ID that was used when
+ * @param[out] order_id where to store the order ID that was used when
* paying for the resource URL
* @param session_id session id
* @param fulfillment_url URL that canonically identifies the resource
@@ -3423,7 +3505,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
",merchant_pub"
",merchant_id"
",merchant_name"
- ",location"
+ ",address"
",jurisdiction"
",default_max_deposit_fee_val"
",default_max_deposit_fee_frac"
@@ -3434,6 +3516,45 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
",default_pay_delay"
" FROM merchant_instances",
0),
+ /* for postgres_insert_instance() */
+ GNUNET_PQ_make_prepare ("insert_instance",
+ "INSERT INTO merchant_instances"
+ "(merchant_pub"
+ ",merchant_id"
+ ",merchant_name"
+ ",address"
+ ",jurisdiction"
+ ",default_max_deposit_fee_val"
+ ",default_max_deposit_fee_frac"
+ ",default_max_wire_fee_val"
+ ",default_max_wire_fee_frac"
+ ",default_wire_fee_amortization"
+ ",default_wire_transfer_delay"
+ ",default_pay_delay)"
+ "VALUES"
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
+ 12),
+ /* for postgres_insert_instance() */
+ GNUNET_PQ_make_prepare ("insert_keys",
+ "INSERT INTO merchant_keys"
+ "(merchant_priv"
+ ",merchant_serial)"
+ " SELECT $1, merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$2",
+ 2),
+ /* for postgres_insert_account() */
+ GNUNET_PQ_make_prepare ("insert_account",
+ "INSERT INTO merchant_accounts"
+ "(merchant_serial"
+ ",h_wire"
+ ",salt"
+ ",payto_uri"
+ ",active)"
+ " SELECT merchant_serial, $2, $3, $4, $5"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1",
+ 5),
/* OLD API: */
#if 0
GNUNET_PQ_make_prepare ("insert_deposit",
@@ -3931,6 +4052,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->rollback = &postgres_rollback;
plugin->commit = &postgres_commit;
plugin->lookup_instances = &postgres_lookup_instances;
+ plugin->insert_instance = &postgres_insert_instance;
+ plugin->insert_account = &postgres_insert_account;
/* old API: */
plugin->store_deposit = &postgres_store_deposit;
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 30941eb0..2d688dde 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -76,9 +76,9 @@ struct TALER_MERCHANTDB_InstanceSettings
char *name;
/**
- * location of the business
+ * Address of the business
*/
- json_t *location;
+ json_t *address;
/**
* jurisdiction of the business
@@ -355,6 +355,38 @@ struct TALER_MERCHANTDB_Plugin
/**
+ * Insert information about an instance into our database.
+ *
+ * @param cls closure
+ * @param merchant_pub public key of the instance
+ * @param merchant_priv private key of the instance
+ * @param is details about the instance
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*insert_instance)(void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const struct TALER_MERCHANTDB_InstanceSettings *is);
+
+ /**
+ * Insert information about an instance's account into our database.
+ *
+ * @param cls closure
+ * @param id identifier of the instance
+ * @param account_details details about the account
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*insert_account)(
+ void *cls,
+ const char *id,
+ const struct TALER_MERCHANTDB_AccountDetails *account_details);
+
+
+ /* ****************** OLD API ******************** */
+
+ /**
* Insert order into db.
*
* @param cls closure