aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2023-11-21 08:06:00 +0100
committerChristian Grothoff <grothoff@gnunet.org>2023-12-23 00:08:57 +0800
commit094327bc906cdeced19a52c86dc0725aad773997 (patch)
treeb459428305452298749cb90438c7a734a64230d1 /src
parent20632c6a2e293a9799114497d63180a105b37065 (diff)
POST /tokenfamilies endpoint
Diffstat (limited to 'src')
-rw-r--r--src/backend/Makefile.am10
-rw-r--r--src/backend/taler-merchant-httpd.c41
-rw-r--r--src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.c73
-rw-r--r--src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.h41
-rw-r--r--src/backend/taler-merchant-httpd_private-get-templates.c3
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c104
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families-SLUG.h41
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families.c85
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families.h41
-rw-r--r--src/backend/taler-merchant-httpd_private-patch-products-ID.h2
-rw-r--r--src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c158
-rw-r--r--src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.h43
-rw-r--r--src/backend/taler-merchant-httpd_private-post-token-families.c229
-rw-r--r--src/backend/taler-merchant-httpd_private-post-token-families.h43
-rw-r--r--src/backenddb/Makefile.am2
-rw-r--r--src/backenddb/merchant-0002.sql45
-rw-r--r--src/backenddb/merchantdb_helper.c10
-rw-r--r--src/backenddb/pg_insert_token_family.c81
-rw-r--r--src/backenddb/pg_insert_token_family.h43
-rw-r--r--src/backenddb/pg_lookup_token_family.c101
-rw-r--r--src/backenddb/pg_lookup_token_family.h44
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c8
-rw-r--r--src/include/taler_merchantdb_plugin.h108
23 files changed, 1332 insertions, 24 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 4a25785c..7978258d 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -53,6 +53,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_private-delete-reserves-ID.h \
taler-merchant-httpd_private-delete-templates-ID.c \
taler-merchant-httpd_private-delete-templates-ID.h \
+ taler-merchant-httpd_private-delete-token-families-SLUG.c \
+ taler-merchant-httpd_private-delete-token-families-SLUG.h \
taler-merchant-httpd_private-delete-transfers-ID.c \
taler-merchant-httpd_private-delete-transfers-ID.h \
taler-merchant-httpd_private-delete-webhooks-ID.c \
@@ -89,6 +91,10 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_private-get-templates.h \
taler-merchant-httpd_private-get-templates-ID.c \
taler-merchant-httpd_private-get-templates-ID.h \
+ taler-merchant-httpd_private-get-token-families.c \
+ taler-merchant-httpd_private-get-token-families.h \
+ taler-merchant-httpd_private-get-token-families-SLUG.c \
+ taler-merchant-httpd_private-get-token-families-SLUG.h \
taler-merchant-httpd_private-get-webhooks.c \
taler-merchant-httpd_private-get-webhooks.h \
taler-merchant-httpd_private-get-webhooks-ID.c \
@@ -105,6 +111,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_private-patch-products-ID.h \
taler-merchant-httpd_private-patch-templates-ID.c \
taler-merchant-httpd_private-patch-templates-ID.h \
+ taler-merchant-httpd_private-patch-token-families-SLUG.c \
+ taler-merchant-httpd_private-patch-token-families-SLUG.h \
taler-merchant-httpd_private-patch-webhooks-ID.c \
taler-merchant-httpd_private-patch-webhooks-ID.h \
taler-merchant-httpd_private-post-account.c \
@@ -131,6 +139,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_private-post-reserves-ID-authorize-reward.h \
taler-merchant-httpd_private-post-templates.c \
taler-merchant-httpd_private-post-templates.h \
+ taler-merchant-httpd_private-post-token-families.c \
+ taler-merchant-httpd_private-post-token-families.h \
taler-merchant-httpd_private-post-transfers.c \
taler-merchant-httpd_private-post-transfers.h \
taler-merchant-httpd_private-post-webhooks.c \
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 3b78677a..83390887 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -41,6 +41,7 @@
#include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
#include "taler-merchant-httpd_private-delete-reserves-ID.h"
#include "taler-merchant-httpd_private-delete-templates-ID.h"
+#include "taler-merchant-httpd_private-delete-token-families-SLUG.h"
#include "taler-merchant-httpd_private-delete-transfers-ID.h"
#include "taler-merchant-httpd_private-delete-webhooks-ID.h"
#include "taler-merchant-httpd_private-get-accounts.h"
@@ -60,6 +61,8 @@
#include "taler-merchant-httpd_private-get-rewards.h"
#include "taler-merchant-httpd_private-get-templates.h"
#include "taler-merchant-httpd_private-get-templates-ID.h"
+#include "taler-merchant-httpd_private-get-token-families.h"
+#include "taler-merchant-httpd_private-get-token-families-SLUG.h"
#include "taler-merchant-httpd_private-get-transfers.h"
#include "taler-merchant-httpd_private-get-webhooks.h"
#include "taler-merchant-httpd_private-get-webhooks-ID.h"
@@ -69,6 +72,7 @@
#include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
#include "taler-merchant-httpd_private-patch-products-ID.h"
#include "taler-merchant-httpd_private-patch-templates-ID.h"
+#include "taler-merchant-httpd_private-patch-token-families-SLUG.h"
#include "taler-merchant-httpd_private-patch-webhooks-ID.h"
#include "taler-merchant-httpd_private-post-account.h"
#include "taler-merchant-httpd_private-post-instances.h"
@@ -82,6 +86,7 @@
#include "taler-merchant-httpd_private-post-reserves.h"
#include "taler-merchant-httpd_private-post-reserves-ID-authorize-reward.h"
#include "taler-merchant-httpd_private-post-templates.h"
+#include "taler-merchant-httpd_private-post-token-families.h"
#include "taler-merchant-httpd_private-post-transfers.h"
#include "taler-merchant-httpd_private-post-webhooks.h"
#include "taler-merchant-httpd_post-orders-ID-abort.h"
@@ -1300,6 +1305,42 @@ url_handler (void *cls,
.method = MHD_HTTP_METHOD_DELETE,
.handler = &TMH_private_delete_instances_ID_token,
},
+ /* GET /tokenfamilies: */
+ {
+ .url_prefix = "/tokenfamilies",
+ .method = MHD_HTTP_METHOD_GET,
+ .handler = &TMH_private_get_tokenfamilies
+ },
+ /* POST /tokenfamilies: */
+ {
+ .url_prefix = "/tokenfamilies",
+ .method = MHD_HTTP_METHOD_POST,
+ .handler = &TMH_private_post_token_families
+ },
+ /* GET /tokenfamilies/$SLUG/: */
+ {
+ .url_prefix = "/tokenfamilies/",
+ .method = MHD_HTTP_METHOD_GET,
+ .have_id_segment = true,
+ .allow_deleted_instance = true,
+ .handler = &TMH_private_get_tokenfamilies_SLUG
+ },
+ /* DELETE /tokenfamilies/$SLUG/: */
+ {
+ .url_prefix = "/tokenfamilies/",
+ .method = MHD_HTTP_METHOD_DELETE,
+ .have_id_segment = true,
+ .allow_deleted_instance = true,
+ .handler = &TMH_private_delete_token_families_SLUG
+ },
+ /* PATCH /tokenfamilies/$SLUG/: */
+ {
+ .url_prefix = "/tokenfamilies/",
+ .method = MHD_HTTP_METHOD_PATCH,
+ .have_id_segment = true,
+ .allow_deleted_instance = true,
+ .handler = &TMH_private_patch_token_family_SLUG,
+ },
{
.url_prefix = NULL
}
diff --git a/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.c
new file mode 100644
index 00000000..3418b795
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.c
@@ -0,0 +1,73 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-delete-token-families-SLUG.c
+ * @brief implement DELETE /tokenfamilies/$SLUG
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-delete-token-families-SLUG.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle a DELETE "/tokenfamilies/$SLUG" request.
+ *
+ * @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_delete_token_families_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+ enum GNUNET_DB_QueryStatus qs;
+
+ (void) rh;
+ GNUNET_assert (NULL != mi);
+ GNUNET_assert (NULL != hc->infix);
+ qs = TMH_db->delete_token_family (TMH_db->cls,
+ mi->settings.id,
+ hc->infix);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "delete_token_family");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "delete_token_family (soft)");
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ GNUNET_assert (0);
+ return MHD_NO;
+}
+
+
+/* end of taler-merchant-httpd_private-delete-token-families-SLUG.c */
diff --git a/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.h b/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.h
new file mode 100644
index 00000000..e8b72fc6
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-delete-token-families-SLUG.h
@@ -0,0 +1,41 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-delete-token-families-SLUG.h
+ * @brief implement DELETE /tokenfamilies/$SLUG/
+ * @author Christian Blättler
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_TOKEN_FAMILIES_SLUG_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_TOKEN_FAMILIES_SLUG_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a DELETE "/tokenfamilies/$SLUG" request.
+ *
+ * @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_delete_token_families_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-delete-token-families-SLUG.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-templates.c b/src/backend/taler-merchant-httpd_private-get-templates.c
index e59e47c9..d0bec884 100644
--- a/src/backend/taler-merchant-httpd_private-get-templates.c
+++ b/src/backend/taler-merchant-httpd_private-get-templates.c
@@ -40,8 +40,7 @@ add_template (void *cls,
json_array_append_new (
pa,
GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("template_id",
- template_id),
+ GNUNET_JSON_pack_string ("template_id", template_id),
GNUNET_JSON_pack_string ("template_description",
template_description))));
}
diff --git a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c
new file mode 100644
index 00000000..d31884da
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c
@@ -0,0 +1,104 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-get-token-families-SLUG.c
+ * @brief implement GET /tokenfamilies/$SLUG/
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-token-families-SLUG.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle a GET "/tokenfamilies/$SLUG" request.
+ *
+ * @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_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+ struct TALER_MERCHANTDB_TokenFamilyDetails details = { 0 };
+ enum GNUNET_DB_QueryStatus status;
+
+ GNUNET_assert (NULL != mi);
+ status = TMH_db->lookup_token_family (TMH_db->cls,
+ mi->settings.id,
+ hc->infix,
+ &details);
+ if (0 > status)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_token_family");
+ }
+ if (0 == status)
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
+ hc->infix);
+ }
+ // TODO: Why is this block here? Limiting variable lifetime?
+ {
+ char *kind = NULL;
+ if (TALER_MERCHANTDB_TFK_Subscription == details.kind)
+ {
+ kind = GNUNET_strdup ("subscription");
+ }
+ else if (TALER_MERCHANTDB_TFK_Discount == details.kind)
+ {
+ kind = GNUNET_strdup ("discount");
+ }
+ else
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ // TODO: What error code to use here?
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "invalid_token_family_kind");
+ }
+
+ MHD_RESULT result;
+
+ result = TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("name", details.name),
+ GNUNET_JSON_pack_string ("description", details.description),
+ GNUNET_JSON_pack_object_steal ("description_i18n", details.description_i18n),
+ GNUNET_JSON_pack_timestamp ("valid_after", details.valid_after),
+ GNUNET_JSON_pack_timestamp ("valid_before", details.valid_before),
+ GNUNET_JSON_pack_time_rel ("duration", details.duration),
+ GNUNET_JSON_pack_string ("kind", kind)
+ );
+
+ GNUNET_free (details.name);
+ GNUNET_free (details.description);
+ return result;
+ }
+}
+
+
+/* end of taler-merchant-httpd_private-get-products-SLUG.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.h b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.h
new file mode 100644
index 00000000..a7b02d8f
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.h
@@ -0,0 +1,41 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-get-token-families-SLUG.h
+ * @brief implement GET /tokenfamilies/$SLUG/
+ * @author Christian Blättler
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_TOKENFAMILIES_SLUG_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_TOKENFAMILIES_SLUG_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/tokenfamilies/$SLUG" request.
+ *
+ * @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_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-token-families-SLUG.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-token-families.c b/src/backend/taler-merchant-httpd_private-get-token-families.c
new file mode 100644
index 00000000..031edd90
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-token-families.c
@@ -0,0 +1,85 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-get-token-families.c
+ * @brief implement GET /tokenfamilies
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-token-families.h"
+
+
+
+/**
+ * Add token family details to our JSON array.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param slug slug of the token family
+ * @param name name of the token family
+ * @param start_time start time of the token family's validity period
+ * @param expiration end time of the token family's validity period
+ */
+static void
+add_token_family (void *cls,
+ const char *slug,
+ const char *name,
+ struct GNUNET_TIME_Timestamp start_time,
+ struct GNUNET_TIME_Timestamp expiration)
+{
+ json_t *pa = cls;
+
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ pa,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("slug", slug),
+ GNUNET_JSON_pack_string ("name", name),
+ GNUNET_JSON_pack_timestamp ("start_time", start_time),
+ GNUNET_JSON_pack_timestamp ("expiration", expiration))));
+}
+
+
+MHD_RESULT
+TMH_private_get_tokenfamilies (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ json_t *families;
+ enum GNUNET_DB_QueryStatus qs;
+
+ families = json_array ();
+ GNUNET_assert (NULL != families);
+ qs = TMH_db->lookup_token_families (TMH_db->cls,
+ hc->instance->settings.id,
+ &add_token_family,
+ families);
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ json_decref (families);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ }
+ return TALER_MHD_REPLY_JSON_PACK (connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("token_families",
+ families));
+}
+
+
+/* end of taler-merchant-httpd_private-get-token-families.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-token-families.h b/src/backend/taler-merchant-httpd_private-get-token-families.h
new file mode 100644
index 00000000..a02a42b0
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-token-families.h
@@ -0,0 +1,41 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-get-token-families.h
+ * @brief implement GET /tokenfamilies
+ * @author Christian Blättler
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_TOKENFAMILIES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_TOKENFAMILIES_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/tokenfamilies" request.
+ *
+ * @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_get_tokenfamilies (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-token-families.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.h b/src/backend/taler-merchant-httpd_private-patch-products-ID.h
index e7f8fcfd..9ce0a7ae 100644
--- a/src/backend/taler-merchant-httpd_private-patch-products-ID.h
+++ b/src/backend/taler-merchant-httpd_private-patch-products-ID.h
@@ -19,7 +19,7 @@
/**
* @file taler-merchant-httpd_private-patch-products-ID.h
- * @brief implementing POST /products request handling
+ * @brief implementing PATCH /products/$ID request handling
* @author Christian Grothoff
*/
#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_PRODUCTS_ID_H
diff --git a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
new file mode 100644
index 00000000..a5fef59d
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
@@ -0,0 +1,158 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-patch-token-families-SLUG.c
+ * @brief implementing PATCH /tokenfamilies/$SLUG request handling
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-patch-token-families-SLUG.h"
+#include "taler-merchant-httpd_helper.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * How often do we retry the simple INSERT database transaction?
+ */
+#define MAX_RETRIES 3
+
+/**
+ * Handle a PATCH "/tokenfamilies/$slug" request.
+ *
+ * @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_patch_token_family_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+ const char *slug = hc->infix;
+ struct TALER_MERCHANTDB_TokenFamilyDetails details = {0};
+ int64_t total_stock;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("name",
+ (const char **) &details.name),
+ GNUNET_JSON_spec_string ("description",
+ (const char **) &details.description),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("description_i18n",
+ &details.description_i18n),
+ NULL),
+ GNUNET_JSON_spec_timestamp ("valid_after",
+ &details.valid_after),
+ GNUNET_JSON_spec_timestamp ("valid_before",
+ &details.valid_before),
+ GNUNET_JSON_spec_relative_time ("duration",
+ &details.duration),
+ GNUNET_JSON_spec_end ()
+ };
+
+ GNUNET_assert (NULL != mi);
+ GNUNET_assert (NULL != slug);
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec);
+ if (GNUNET_OK != res)
+ return (GNUNET_NO == res)
+ ? MHD_YES
+ : MHD_NO;
+ }
+
+ struct GNUNET_TIME_Relative validity = GNUNET_TIME_absolute_get_difference(
+ details.valid_after.abs_time,
+ details.valid_before.abs_time);
+
+ // Check if start_time is before valid_before
+ if (0 == validity.rel_value_us)
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid_validity_duration");
+ }
+
+ if (NULL == details.description_i18n)
+ details.description_i18n = json_object ();
+
+ if (! TALER_JSON_check_i18n (details.description_i18n))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "description_i18n");
+ }
+
+ qs = TMH_db->update_token_family (TMH_db->cls,
+ mi->settings.id,
+ slug,
+ &details);
+ {
+ MHD_RESULT ret = MHD_NO;
+
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ NULL);
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected serialization problem");
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ // TODO: Add error code for token family not found
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ 0,
+ slug);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ ret = TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ break;
+ }
+ GNUNET_JSON_parse_free (spec);
+ return ret;
+ }
+}
+
+
+/* end of taler-merchant-httpd_private-patch-token-families-SLUG.c */
diff --git a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.h b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.h
new file mode 100644
index 00000000..87ad86b3
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-patch-token-families-SLUG.h
+ * @brief implementing PATCH /tokenfamilies/$SLUG request handling
+ * @author Christian Blättler
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_TOKEN_FAMILIES_SLUG_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_TOKEN_FAMILIES_SLUG_H
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a PATCH "/tokenfamilies/$slug" request.
+ *
+ * @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_patch_token_family_SLUG (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-post-token-families.c b/src/backend/taler-merchant-httpd_private-post-token-families.c
new file mode 100644
index 00000000..6ff25942
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-token-families.c
@@ -0,0 +1,229 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-post-token-families.c
+ * @brief implementing POST /tokenfamilies request handling
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-post-token-families.h"
+#include "taler-merchant-httpd_helper.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * How often do we retry the simple INSERT database transaction?
+ */
+#define MAX_RETRIES 3
+
+
+/**
+ * Check if the two token families are identical.
+ *
+ * @param tf1 token family to compare
+ * @param tf2 other token family to compare
+ * @return true if they are 'equal', false if not
+ */
+static bool
+token_families_equal (const struct TALER_MERCHANTDB_TokenFamilyDetails *tf1,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *tf2)
+{
+ return ( (0 == strcmp (tf1->slug,
+ tf2->slug)) &&
+ (0 == strcmp (tf1->name,
+ tf2->name)) &&
+ (0 == strcmp (tf1->description,
+ tf2->description)) &&
+ (1 == json_equal (tf1->description_i18n,
+ tf2->description_i18n)) &&
+ (GNUNET_TIME_timestamp_cmp (tf1->valid_after,
+ ==,
+ tf2->valid_after)) &&
+ (GNUNET_TIME_timestamp_cmp (tf1->valid_before,
+ ==,
+ tf2->valid_before)) &&
+ (GNUNET_TIME_relative_cmp (tf1->duration,
+ ==,
+ tf2->duration)) &&
+ (tf1->kind == tf2->kind) );
+}
+
+
+MHD_RESULT
+TMH_private_post_token_families (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct TMH_MerchantInstance *mi = hc->instance;
+ struct TALER_MERCHANTDB_TokenFamilyDetails details = { 0 };
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("slug",
+ (const char **) &details.slug),
+ GNUNET_JSON_spec_string ("name",
+ (const char **) &details.name),
+ GNUNET_JSON_spec_string ("description",
+ (const char **) &details.description),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("description_i18n",
+ &details.description_i18n),
+ NULL),
+ GNUNET_JSON_spec_uint32("kind", &details.kind),
+ GNUNET_JSON_spec_timestamp ("valid_after",
+ &details.valid_after),
+ GNUNET_JSON_spec_timestamp ("valid_before",
+ &details.valid_before),
+ GNUNET_JSON_spec_relative_time ("duration",
+ &details.duration),
+ GNUNET_JSON_spec_end ()
+ };
+
+ GNUNET_assert (NULL != mi);
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_NO == res)
+ ? MHD_YES
+ : MHD_NO;
+ }
+ }
+
+ if (NULL == details.description_i18n)
+ details.description_i18n = json_object ();
+
+ if (! TALER_JSON_check_i18n (details.description_i18n))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "description_i18n");
+ }
+
+
+ /* finally, interact with DB until no serialization error */
+ for (unsigned int i = 0; i<MAX_RETRIES; i++)
+ {
+ /* Test if a token family of this id is known */
+ struct TALER_MERCHANTDB_TokenFamilyDetails existing;
+
+ if (GNUNET_OK !=
+ TMH_db->start (TMH_db->cls,
+ "/post tokenfamilies"))
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_START_FAILED,
+ NULL);
+ }
+ qs = TMH_db->lookup_token_family (TMH_db->cls,
+ mi->settings.id,
+ details.slug,
+ &existing);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ /* Clean up and fail hard */
+ GNUNET_break (0);
+ TMH_db->rollback (TMH_db->cls);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* restart transaction */
+ goto retry;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* Good, we can proceed! */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* idempotency check: is existing == details? */
+ {
+ bool eq;
+
+ eq = token_families_equal (&details,
+ &existing);
+ TALER_MERCHANTDB_token_family_details_free (&existing);
+ TMH_db->rollback (TMH_db->cls);
+ GNUNET_JSON_parse_free (spec);
+ return eq
+ ? TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0)
+ // TODO: Use proper error code
+ : TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ 0,
+ details.slug);
+ }
+ } /* end switch (qs) */
+
+ qs = TMH_db->insert_token_family (TMH_db->cls,
+ mi->settings.id,
+ details.slug,
+ &details);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ break;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ qs = TMH_db->commit (TMH_db->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+ break;
+ }
+retry:
+ GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_db->rollback (TMH_db->cls);
+ } /* for RETRIES loop */
+ GNUNET_JSON_parse_free (spec);
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ ? TALER_EC_GENERIC_DB_SOFT_FAILURE
+ : TALER_EC_GENERIC_DB_COMMIT_FAILED,
+ NULL);
+ }
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+}
+
+
+/* end of taler-merchant-httpd_private-post-token-families.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-token-families.h b/src/backend/taler-merchant-httpd_private-post-token-families.h
new file mode 100644
index 00000000..ada1c7c9
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-token-families.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of TALER
+ (C) 2023 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 taler-merchant-httpd_private-post-token-families.h
+ * @brief implementing POST /tokenfamilies request handling
+ * @author Christian Blättler
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_TOKEN_FAMILIES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_POST_TOKEN_FAMILIES_H
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Create a new token family.
+ *
+ * @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_token_families (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 339d59af..f0e2bc9c 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -166,6 +166,8 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
pg_insert_pending_webhook.h pg_insert_pending_webhook.c \
pg_update_pending_webhook.h pg_update_pending_webhook.c \
pg_lookup_pending_webhooks.h pg_lookup_pending_webhooks.c \
+ pg_insert_token_family.h pg_insert_token_family.c \
+ pg_lookup_token_family.h pg_lookup_token_family.c \
plugin_merchantdb_postgres.c \
pg_helper.h pg_helper.c
libtaler_plugin_merchantdb_postgres_la_LIBADD = \
diff --git a/src/backenddb/merchant-0002.sql b/src/backenddb/merchant-0002.sql
index c2fffb43..b92a1fcf 100644
--- a/src/backenddb/merchant-0002.sql
+++ b/src/backenddb/merchant-0002.sql
@@ -14,6 +14,10 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
+-- @file merchant-0002.sql
+-- @brief database schema for the merchant
+-- @author Christian Blättler
+
-- Everything in one big transaction
BEGIN;
@@ -43,9 +47,12 @@ CREATE INDEX IF NOT EXISTS merchant_contract_terms_by_merchant_and_session
CREATE TABLE IF NOT EXISTS merchant_token_families
(token_family_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
- ,name TEXT NOT NULL UNIQUE
+ ,slug TEXT NOT NULL UNIQUE
+ ,name TEXT NOT NULL
,description TEXT
,description_i18n BYTEA NOT NULL
+ ,valid_after BIGINT NOT NULL
+ ,valid_before BIGINT NOT NULL
,duration BIGINT NOT NULL
,kind TEXT NOT NULL CHECK (kind IN ('subscription', 'discount'))
,issued BIGINT DEFAULT 0
@@ -53,14 +60,20 @@ CREATE TABLE IF NOT EXISTS merchant_token_families
);
COMMENT ON TABLE merchant_token_families
IS 'Token families configured by the merchant.';
+COMMENT ON COLUMN merchant_token_families.slug
+ IS 'Unique slug for the token family.';
COMMENT ON COLUMN merchant_token_families.name
IS 'Name of the token family.';
COMMENT ON COLUMN merchant_token_families.description
IS 'Human-readable description or details about the token family.';
COMMENT ON COLUMN merchant_token_families.description_i18n
IS 'JSON map from IETF BCP 47 language tags to localized descriptions';
+COMMENT ON COLUMN merchant_token_families.valid_after
+ IS 'Start time of the token family''s validity period.';
+COMMENT ON COLUMN merchant_token_families.valid_before
+ IS 'End time of the token family''s validity period.';
COMMENT ON COLUMN merchant_token_families.duration
- IS 'Duration of the token family.';
+ IS 'Duration of the token.';
COMMENT ON COLUMN merchant_token_families.kind
IS 'Kind of the token (e.g., subscription, discount).';
COMMENT ON COLUMN merchant_token_families.issued
@@ -69,8 +82,8 @@ COMMENT ON COLUMN merchant_token_families.redeemed
IS 'Counter for the number of tokens redeemed for this token family.';
-CREATE TABLE IF NOT EXISTS merchant_token_keys
- (token_keys_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
+CREATE TABLE IF NOT EXISTS merchant_token_family_keys
+ (token_family_key_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
,token_family_serial BIGINT REFERENCES merchant_token_families(token_family_serial) ON DELETE CASCADE
,valid_after BIGINT NOT NULL
,valid_before BIGINT NOT NULL
@@ -81,21 +94,21 @@ CREATE TABLE IF NOT EXISTS merchant_token_keys
,UNIQUE (token_family_serial, valid_after)
);
-COMMENT ON TABLE merchant_token_keys
+COMMENT ON TABLE merchant_token_family_keys
IS 'Keys for token families.';
-COMMENT ON COLUMN merchant_token_keys.token_family_serial
+COMMENT ON COLUMN merchant_token_family_keys.token_family_serial
IS 'Token family to which the key belongs.';
-COMMENT ON COLUMN merchant_token_keys.valid_after
- IS 'Start date/time for the validity of the token key.';
-COMMENT ON COLUMN merchant_token_keys.valid_before
- IS 'Expiration date/time for the validity of the token key.';
-COMMENT ON COLUMN merchant_token_keys.pub
+COMMENT ON COLUMN merchant_token_family_keys.valid_after
+ IS 'Start time for the validity of the token key.';
+COMMENT ON COLUMN merchant_token_family_keys.valid_before
+ IS 'Expiration time for the validity of the token key.';
+COMMENT ON COLUMN merchant_token_family_keys.pub
IS 'Public key of the token family.';
-COMMENT ON COLUMN merchant_token_keys.h_pub
+COMMENT ON COLUMN merchant_token_family_keys.h_pub
IS 'Hash of the public key for quick lookup.';
-COMMENT ON COLUMN merchant_token_keys.priv
+COMMENT ON COLUMN merchant_token_family_keys.priv
IS 'Private key of the token family; can be NULL if no more tokens of this familiy should be issued.';
-COMMENT ON COLUMN merchant_token_keys.cipher
+COMMENT ON COLUMN merchant_token_family_keys.cipher
IS 'Cipher used (rsa or cs).';
@@ -103,7 +116,7 @@ CREATE TABLE IF NOT EXISTS merchant_spent_tokens
(spent_token_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
,merchant_serial BIGINT NOT NULL REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
- ,token_key_serial BIGINT REFERENCES merchant_token_key(token_key_serial) ON DELETE CASCADE
+ ,token_family_key_serial BIGINT REFERENCES merchant_token_family_keys(token_family_key_serial) ON DELETE CASCADE
,token_pub BYTEA NOT NULL UNIQUE CHECK (LENGTH(token_pub)=32)
,token_sig BYTEA NOT NULL CHECK (LENGTH(token_sig)=64)
,blind_sig BYTEA NOT NULL
@@ -114,7 +127,7 @@ COMMENT ON COLUMN merchant_spent_tokens.merchant_serial
IS 'Merchant serial where the token was spent.';
COMMENT ON COLUMN merchant_spent_tokens.h_contract_terms
IS 'This is no foreign key by design.';
-COMMENT ON COLUMN merchant_spent_tokens.token_key_serial
+COMMENT ON COLUMN merchant_spent_tokens.token_family_key_serial
IS 'Token family to which the spent token belongs.';
COMMENT ON COLUMN merchant_spent_tokens.token_pub
IS 'Public key of the spent token.';
diff --git a/src/backenddb/merchantdb_helper.c b/src/backenddb/merchantdb_helper.c
index 4ae75020..4ba70e4b 100644
--- a/src/backenddb/merchantdb_helper.c
+++ b/src/backenddb/merchantdb_helper.c
@@ -69,5 +69,15 @@ TALER_MERCHANTDB_pending_webhook_details_free (
GNUNET_free (pwb->body);
}
+void
+TALER_MERCHANTDB_token_family_details_free (
+ struct TALER_MERCHANTDB_TokenFamilyDetails *tf)
+{
+ GNUNET_free (tf->slug);
+ GNUNET_free (tf->name);
+ GNUNET_free (tf->description);
+ json_decref (tf->description_i18n);
+}
+
/* end of merchantdb_helper.c */
diff --git a/src/backenddb/pg_insert_token_family.c b/src/backenddb/pg_insert_token_family.c
new file mode 100644
index 00000000..88cd8649
--- /dev/null
+++ b/src/backenddb/pg_insert_token_family.c
@@ -0,0 +1,81 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 backenddb/pg_insert_token_family.c
+ * @brief Implementation of the insert_token_family function for Postgres
+ * @author Christian Blättler
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_token_family.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_token_family (void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details)
+{
+ struct PostgresClosure *pg = cls;
+
+ const char *kind;
+ switch (details->kind)
+ {
+ case TALER_MERCHANTDB_TFK_Discount:
+ kind = "discount";
+ break;
+ case TALER_MERCHANTDB_TFK_Subscription:
+ kind = "subscription";
+ default:
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ struct GNUNET_PQ_QueryParam params[] = {
+ // GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_string (token_family_slug),
+ GNUNET_PQ_query_param_string (details->name),
+ GNUNET_PQ_query_param_string (details->description),
+ TALER_PQ_query_param_json (details->description_i18n),
+ GNUNET_PQ_query_param_timestamp (&details->valid_after),
+ GNUNET_PQ_query_param_timestamp (&details->valid_before),
+ GNUNET_PQ_query_param_relative_time (&details->duration),
+ GNUNET_PQ_query_param_string (kind),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "insert_token_family",
+ "INSERT INTO merchant_token_families"
+ "(slug"
+ ",name"
+ ",description"
+ ",description_i18n"
+ ",valid_after"
+ ",valid_before"
+ ",duration"
+ ",kind)"
+ "VALUES"
+ "($1, $2, $3, $4, $5, $6, $7, $8)");
+ // " FROM merchant_instances"
+ // " WHERE merchant_id=$1");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_token_family",
+ params);
+}
diff --git a/src/backenddb/pg_insert_token_family.h b/src/backenddb/pg_insert_token_family.h
new file mode 100644
index 00000000..e05755a6
--- /dev/null
+++ b/src/backenddb/pg_insert_token_family.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 backenddb/pg_insert_token_family.h
+ * @brief implementation of the insert_token_family function for Postgres
+ * @author Christian Blättler
+ */
+#ifndef PG_INSERT_TOKEN_FAMILY_H
+#define PG_INSERT_TOKEN_FAMILY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * @param cls closure
+ * @param instance_id instance to insert token family for TODO: Is this needed?
+ * @param token_family_slug slug of the token family to insert
+ * @param details the token family details to insert
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_token_family (void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
+#endif
+
diff --git a/src/backenddb/pg_lookup_token_family.c b/src/backenddb/pg_lookup_token_family.c
new file mode 100644
index 00000000..7a871692
--- /dev/null
+++ b/src/backenddb/pg_lookup_token_family.c
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 backenddb/pg_lookup_token_family.c
+ * @brief Implementation of the lookup_token_family function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_lookup_token_family.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_token_family (void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ struct TALER_MERCHANTDB_TokenFamilyDetails *details)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ // GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_string (token_family_slug),
+ GNUNET_PQ_query_param_end
+ };
+
+ if (NULL == details)
+ {
+ struct GNUNET_PQ_ResultSpec rs_null[] = {
+ GNUNET_PQ_result_spec_end
+ };
+
+ check_connection (pg);
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_token_family",
+ params,
+ rs_null);
+ }
+ else
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_string ("slug",
+ &details->slug),
+ GNUNET_PQ_result_spec_string ("name",
+ &details->name),
+ GNUNET_PQ_result_spec_string ("description",
+ &details->description),
+ TALER_PQ_result_spec_json ("description_i18n",
+ &details->description_i18n),
+ GNUNET_PQ_result_spec_timestamp ("valid_after",
+ &details->valid_after),
+ GNUNET_PQ_result_spec_timestamp ("valid_before",
+ &details->valid_before),
+ GNUNET_PQ_result_spec_relative_time ("duration",
+ &details->duration),
+ // TODO: How to convert kind string into enum TALER_MERCHANTDB_TokenFamilyKind
+ GNUNET_PQ_result_spec_uint64 ("issued",
+ &details->issued),
+ GNUNET_PQ_result_spec_uint64 ("redeemed",
+ &details->redeemed),
+ GNUNET_PQ_result_spec_end
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "lookup_token_family",
+ "SELECT"
+ " slug"
+ ",name"
+ ",description"
+ ",description_i18n"
+ ",valid_after"
+ ",valid_before"
+ ",duration"
+ ",issued"
+ ",redeemed"
+ " FROM merchant_token_families"
+ // " JOIN merchant_instances"
+ // " USING (merchant_serial)"
+ // " WHERE merchant_instances.merchant_id=$1"
+ " WHERE merchant_token_families.slug=$1");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_token_family",
+ params,
+ rs);
+ }
+}
diff --git a/src/backenddb/pg_lookup_token_family.h b/src/backenddb/pg_lookup_token_family.h
new file mode 100644
index 00000000..4a1b1872
--- /dev/null
+++ b/src/backenddb/pg_lookup_token_family.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 backenddb/pg_lookup_token_family.h
+ * @brief implementation of the lookup_token_family function for Postgres
+ * @author Christian Blättler
+ */
+#ifndef PG_LOOKUP_TOKEN_FAMILY_H
+#define PG_LOOKUP_TOKEN_FAMILY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Lookup details about a particular token family.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup token family for
+ * @param token_family_slug token family to lookup
+ * @param[out] details set to the token family details on success, can be NULL
+ * (in that case we only want to check if the token family exists)
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_token_family (void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
+#endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index 1f1f54c6..90586561 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -147,6 +147,8 @@
#include "pg_activate_reserve.h"
#include "pg_authorize_reward.h"
#include "pg_insert_pickup.h"
+#include "pg_insert_token_family.h"
+#include "pg_lookup_token_family.h"
/**
@@ -601,6 +603,12 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
= &TMH_PG_select_accounts_by_exchange;
plugin->insert_exchange_account
= &TMH_PG_insert_exchange_account;
+ plugin->insert_token_family
+ = &TMH_PG_insert_token_family;
+ plugin->lookup_token_family
+ = &TMH_PG_lookup_token_family;
+
+
return plugin;
}
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 631cdcd5..c838a175 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -255,6 +255,22 @@ typedef void
(*TALER_MERCHANTDB_ProductsCallback)(void *cls,
const char *product_id);
+/**
+ * Typically called by `lookup_token_families`.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param slug slug of the token family
+ * @param name name of the token family
+ * @param start_time start time of the token family's validity period
+ * @param expiration end time of the token family's validity period
+ */
+typedef void
+(*TALER_MERCHANTDB_TokenFamiliesCallback)(void *cls,
+ const char *slug,
+ const char *name,
+ struct GNUNET_TIME_Timestamp start_time,
+ struct GNUNET_TIME_Timestamp expiration);
+
/**
* Details about a product.
@@ -1116,6 +1132,16 @@ struct TALER_MERCHANTDB_TokenFamilyDetails
json_t *description_i18n;
/**
+ * Start time of the token family duration.
+ */
+ struct GNUNET_TIME_Timestamp valid_after;
+
+ /**
+ * End time of the token family duration.
+ */
+ struct GNUNET_TIME_Timestamp valid_before;
+
+ /**
* Validity duration of the token family.
*/
struct GNUNET_TIME_Relative duration;
@@ -1166,11 +1192,6 @@ struct TALER_MERCHANTDB_TokenFamilyKeyDetails
* Token family private key.
*/
struct TALER_TokenFamilyPrivateKey priv;
-
- /**
- * Token family key cipher.
- */
- // enum TALER_MERCHANTDB_TokenKeyCipher cipher;
};
/**
@@ -3540,6 +3561,83 @@ struct TALER_MERCHANTDB_Plugin
(*insert_exchange_keys)(void *cls,
const struct TALER_EXCHANGE_Keys *keys);
+
+ /**
+ * Lookup all of the token families the given instance has configured.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup token families for
+ * @param cb function to call on all token families found
+ * @param cb_cls closure for @a cb
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_token_families)(void *cls,
+ const char *instance_id,
+ TALER_MERCHANTDB_TokenFamiliesCallback cb,
+ void *cb_cls);
+
+ /**
+ * Lookup details about a particular token family.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup token family for
+ * @param token_family_slug token family to lookup
+ * @param[out] details set to the token family details on success, can be NULL
+ * (in that case we only want to check if the token family exists)
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_token_family)(void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
+ /**
+ * Delete information about a token family.
+ *
+ * @param cls closure
+ * @param instance_id instance to delete token family of
+ * @param token_family_slug slug of token family to delete
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*delete_token_family)(void *cls,
+ const char *instance_id,
+ const char *token_family_slug);
+
+ /**
+ * Update details about a particular token family.
+ *
+ * @param cls closure
+ * @param instance_id instance to update token family for
+ * @param token_family_slug slug of token family to update
+ * @param details set to the updated token family on success, can be NULL
+ * (in that case we only want to check if the token family exists)
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*update_token_family)(void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
+
+ /**
+ * Insert details about a particular token family.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert product for
+ * @param token_family_slug slug of token family to insert
+ * @param details the token family details to insert
+ * @return database result code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*insert_token_family)(void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
};
#endif