diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-04-18 13:34:48 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-04-18 13:34:48 +0200 |
commit | 741f7127849d8e5c8e5feacc34fc16d415fc2944 (patch) | |
tree | b15b02519eca43db7f8742e9bf68ee2d02f73375 | |
parent | b03f6cf5044193931b028e867642f13ee9a24f3b (diff) | |
download | merchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.tar.gz merchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.tar.bz2 merchant-741f7127849d8e5c8e5feacc34fc16d415fc2944.zip |
implement POST /instances
-rw-r--r-- | src/backend/Makefile.am | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 41 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 27 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-get-instances.c | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-instances.c | 411 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-instances.h | 43 | ||||
-rw-r--r-- | src/backenddb/merchant-0001.sql | 10 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 131 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 36 |
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 |