gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: start on implementing protocol v


From: gnunet
Subject: [taler-merchant] branch master updated: start on implementing protocol v16
Date: Sat, 25 May 2024 21:40:00 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 3da8f041 start on implementing protocol v16
3da8f041 is described below

commit 3da8f0416e8a3f449db34d54dd4c3f359471bb7e
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat May 25 21:39:57 2024 +0200

    start on implementing protocol v16
---
 ...-merchant-httpd_private-delete-categories-ID.c} |  44 +++--
 ...r-merchant-httpd_private-delete-categories-ID.h |  42 +++++
 ...-merchant-httpd_private-delete-otp-devices-ID.c |   2 +-
 ...aler-merchant-httpd_private-get-categories-ID.c | 111 ++++++++++++
 ...aler-merchant-httpd_private-get-categories-ID.h |  42 +++++
 .../taler-merchant-httpd_private-get-categories.c  |  80 +++++++++
 .../taler-merchant-httpd_private-get-categories.h  |  42 +++++
 ...er-merchant-httpd_private-patch-categories-ID.c | 114 ++++++++++++
 ...er-merchant-httpd_private-patch-categories-ID.h |  45 +++++
 .../taler-merchant-httpd_private-post-categories.c | 198 +++++++++++++++++++++
 .../taler-merchant-httpd_private-post-categories.h |  45 +++++
 src/backenddb/Makefile.am                          |   8 +-
 src/backenddb/merchant-0006.sql                    |  55 ++++++
 .../{pg_delete_otp.c => pg_delete_category.c}      |  26 +--
 src/backenddb/pg_delete_category.h                 |  43 +++++
 src/backenddb/pg_delete_otp.c                      |   4 +-
 src/backenddb/pg_insert_category.c                 |  65 +++++++
 src/backenddb/pg_insert_category.h                 |  46 +++++
 src/backenddb/pg_lookup_categories.c               | 148 +++++++++++++++
 src/backenddb/pg_lookup_categories.h               |  43 +++++
 src/backenddb/pg_select_category.c                 |  37 ++++
 src/backenddb/pg_select_category.h                 |  45 +++++
 .../{pg_delete_otp.c => pg_update_category.c}      |  39 ++--
 src/backenddb/pg_update_category.h                 |  47 +++++
 src/backenddb/pg_update_otp.c                      |  15 +-
 src/backenddb/plugin_merchantdb_postgres.c         |  15 ++
 src/include/taler_merchantdb_plugin.h              | 170 +++++++++++++++++-
 27 files changed, 1499 insertions(+), 72 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c 
b/src/backend/taler-merchant-httpd_private-delete-categories-ID.c
similarity index 65%
copy from src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
copy to src/backend/taler-merchant-httpd_private-delete-categories-ID.c
index b147b84f..47aab5a0 100644
--- a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
+++ b/src/backend/taler-merchant-httpd_private-delete-categories-ID.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2022 Taler Systems SA
+  (C) 2024 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
@@ -14,17 +14,17 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-merchant-httpd_private-delete-otp-devices-ID.c
- * @brief implement DELETE /otp-devices/$ID
+ * @file taler-merchant-httpd_private-delete-categories-ID.c
+ * @brief implement DELETE /private/categories/$ID
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
+#include "taler-merchant-httpd_private-delete-categories-ID.h"
 #include <taler/taler_json_lib.h>
 
 
 /**
- * Handle a DELETE "/otp-devices/$ID" request.
+ * Handle a DELETE "/categories/$ID" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -32,36 +32,50 @@
  * @return MHD result code
  */
 MHD_RESULT
-TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh,
-                                   struct MHD_Connection *connection,
-                                   struct TMH_HandlerContext *hc)
+TMH_private_delete_categories_ID (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi = hc->instance;
   enum GNUNET_DB_QueryStatus qs;
+  unsigned long long cnum;
+  char dummy;
 
   (void) rh;
   GNUNET_assert (NULL != mi);
   GNUNET_assert (NULL != hc->infix);
-  qs = TMH_db->delete_otp (TMH_db->cls,
-                           mi->settings.id,
-                           hc->infix);
+  if (1 != sscanf (hc->infix,
+                   "%llu%d",
+                   &cnum,
+                   &dummy))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                       "category_id must be a number");
+  }
+  qs = TMH_db->delete_category (TMH_db->cls,
+                                mi->settings.id,
+                                cnum);
   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_otp");
+                                       "delete_category");
   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_otp (soft)");
+                                       "delete_category");
   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_NOT_FOUND,
-                                       
TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+                                       
TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN,
                                        hc->infix);
   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     return TALER_MHD_reply_static (connection,
@@ -75,4 +89,4 @@ TMH_private_delete_otp_devices_ID (const struct 
TMH_RequestHandler *rh,
 }
 
 
-/* end of taler-merchant-httpd_private-delete-otp-devices-ID.c */
+/* end of taler-merchant-httpd_private-delete-categories-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-delete-categories-ID.h 
b/src/backend/taler-merchant-httpd_private-delete-categories-ID.h
new file mode 100644
index 00000000..b17eed49
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-delete-categories-ID.h
@@ -0,0 +1,42 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories-ID.h
+ * @brief implement DELETE /private/categories/$ID/
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_CATEGORIES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_CATEGORIES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a DELETE "/categories/$ID" 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_categories_ID (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-delete-categories-ID.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c 
b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
index b147b84f..ff9ad2ef 100644
--- a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
+++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
@@ -61,7 +61,7 @@ TMH_private_delete_otp_devices_ID (const struct 
TMH_RequestHandler *rh,
   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_NOT_FOUND,
-                                       
TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+                                       
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
                                        hc->infix);
   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     return TALER_MHD_reply_static (connection,
diff --git a/src/backend/taler-merchant-httpd_private-get-categories-ID.c 
b/src/backend/taler-merchant-httpd_private-get-categories-ID.c
new file mode 100644
index 00000000..bebe087f
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-categories-ID.c
@@ -0,0 +1,111 @@
+/*
+  This file is part of TALER
+  (C) 2022-2024 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-categories-ID.c
+ * @brief implement GET /private/categories/$ID
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-categories-ID.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle a GET "/private/categories/$ID" 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_categories_ID (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 };
+  enum GNUNET_DB_QueryStatus qs;
+  uint64_t faketime_s
+    = GNUNET_TIME_timestamp_to_s (GNUNET_TIME_timestamp_get ());
+  struct GNUNET_TIME_Timestamp my_time;
+  struct TALER_Amount price;
+
+  TALER_MHD_parse_request_number (connection,
+                                  "faketime",
+                                  &faketime_s);
+  memset (&price,
+          0,
+          sizeof (price));
+  TALER_MHD_parse_request_amount (connection,
+                                  "price",
+                                  &price);
+  my_time = GNUNET_TIME_timestamp_from_s (faketime_s);
+  GNUNET_assert (NULL != mi);
+  qs = TMH_db->select_otp (TMH_db->cls,
+                           mi->settings.id,
+                           hc->infix,
+                           &tp);
+  if (0 > qs)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                       "select_otp");
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       
TALER_EC_MERCHANT_GENERIC_CATEGORY_UNKNOWN,
+                                       hc->infix);
+  }
+  {
+    MHD_RESULT ret;
+    char *pos_confirmation;
+
+    pos_confirmation = (NULL == tp.otp_key)
+      ? NULL
+      : TALER_build_pos_confirmation (tp.otp_key,
+                                      tp.otp_algorithm,
+                                      &price,
+                                      my_time);
+    /* Note: we deliberately (by design) do not return the otp_key */
+    ret = TALER_MHD_REPLY_JSON_PACK (
+      connection,
+      MHD_HTTP_OK,
+      GNUNET_JSON_pack_string ("device_description",
+                               tp.otp_description),
+      GNUNET_JSON_pack_allow_null (
+        GNUNET_JSON_pack_string ("otp_code",
+                                 pos_confirmation)),
+      GNUNET_JSON_pack_uint64 ("otp_timestamp",
+                               faketime_s),
+      GNUNET_JSON_pack_uint64 ("otp_algorithm",
+                               tp.otp_algorithm),
+      GNUNET_JSON_pack_uint64 ("otp_ctr",
+                               tp.otp_ctr));
+    GNUNET_free (pos_confirmation);
+    GNUNET_free (tp.otp_description);
+    GNUNET_free (tp.otp_key);
+    return ret;
+  }
+}
+
+
+/* end of taler-merchant-httpd_private-get-categories-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-categories-ID.h 
b/src/backend/taler-merchant-httpd_private-get-categories-ID.h
new file mode 100644
index 00000000..c0226659
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-categories-ID.h
@@ -0,0 +1,42 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories-ID.h
+ * @brief implement GET /private/categories/$ID/
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/private/categories/$ID" 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_categories_ID (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-categories-ID.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-categories.c 
b/src/backend/taler-merchant-httpd_private-get-categories.c
new file mode 100644
index 00000000..74c0d6cd
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-categories.c
@@ -0,0 +1,80 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories.c
+ * @brief implement GET /categories
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-categories.h"
+
+
+/**
+ * Add OTP device details to our JSON array.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param otp_id ID of the OTP device
+ * @param otp_description human-readable description for the OTP device
+ */
+static void
+add_otp (void *cls,
+         const char *otp_id,
+         const char *otp_description)
+{
+  json_t *pa = cls;
+
+  GNUNET_assert (0 ==
+                 json_array_append_new (
+                   pa,
+                   GNUNET_JSON_PACK (
+                     GNUNET_JSON_pack_string ("otp_device_id",
+                                              otp_id),
+                     GNUNET_JSON_pack_string ("device_description",
+                                              otp_description))));
+}
+
+
+MHD_RESULT
+TMH_private_get_categories (const struct TMH_RequestHandler *rh,
+                            struct MHD_Connection *connection,
+                            struct TMH_HandlerContext *hc)
+{
+  json_t *pa;
+  enum GNUNET_DB_QueryStatus qs;
+
+  pa = json_array ();
+  GNUNET_assert (NULL != pa);
+  qs = TMH_db->lookup_categories (TMH_db->cls,
+                                  hc->instance->settings.id,
+                                  &add_otp,
+                                  pa);
+  if (0 > qs)
+  {
+    GNUNET_break (0);
+    json_decref (pa);
+    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 ("categories",
+                                                                  pa));
+}
+
+
+/* end of taler-merchant-httpd_private-get-categories.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-categories.h 
b/src/backend/taler-merchant-httpd_private-get-categories.h
new file mode 100644
index 00000000..68eed05e
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-categories.h
@@ -0,0 +1,42 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories.h
+ * @brief implement GET /private/categories
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_CATEGORIES_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/private/categories" 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_categories (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-categories.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-patch-categories-ID.c 
b/src/backend/taler-merchant-httpd_private-patch-categories-ID.c
new file mode 100644
index 00000000..596b8b09
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-categories-ID.c
@@ -0,0 +1,114 @@
+/*
+  This file is part of TALER
+  (C) 2022 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-otp-devices-ID.c
+ * @brief implementing PATCH /otp-devices/$ID request handling
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
+#include "taler-merchant-httpd_helper.h"
+#include <taler/taler_json_lib.h>
+
+
+MHD_RESULT
+TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  const char *device_id = hc->infix;
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = {0};
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("otp_device_description",
+                             (const char **) &tp.otp_description),
+    TALER_JSON_spec_otp_type ("otp_algorithm",
+                              &tp.otp_algorithm),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_uint64 ("otp_ctr",
+                               &tp.otp_ctr),
+      NULL),
+    GNUNET_JSON_spec_mark_optional(
+
+      TALER_JSON_spec_otp_key ("otp_key",
+                               (const char **) &tp.otp_key),
+      NULL),
+    GNUNET_JSON_spec_end ()
+  };
+
+  GNUNET_assert (NULL != mi);
+  GNUNET_assert (NULL != device_id);
+  {
+    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;
+  }
+
+  qs = TMH_db->update_otp (TMH_db->cls,
+                           mi->settings.id,
+                           device_id,
+                           &tp);
+  {
+    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,
+                                        "update_pos");
+      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:
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_NOT_FOUND,
+                                        
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
+                                        device_id);
+      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-otp-devices-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-patch-categories-ID.h 
b/src/backend/taler-merchant-httpd_private-patch-categories-ID.h
new file mode 100644
index 00000000..c88290a8
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-categories-ID.h
@@ -0,0 +1,45 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories-ID.h
+ * @brief implementing PATCH /private/categories/$ID request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_CATEGORIES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_CATEGORIES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * PATCH descriptions of an existing product category.
+ *
+ * @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_categories_ID (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-post-categories.c 
b/src/backend/taler-merchant-httpd_private-post-categories.c
new file mode 100644
index 00000000..255fd2c4
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-categories.c
@@ -0,0 +1,198 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories.c
+ * @brief implementing POST /private/categories request handling
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-post-categories.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 categories are identical.
+ *
+ * @param t1 device to compare
+ * @param t2 other device to compare
+ * @return true if they are 'equal', false if not or of payto_uris is not an 
array
+ */
+static bool
+categories_equal (const struct TALER_MERCHANTDB_OtpDeviceDetails *t1,
+                  const struct TALER_MERCHANTDB_OtpDeviceDetails *t2)
+{
+  return ( (0 == strcmp (t1->otp_description,
+                         t2->otp_description)) &&
+           (0 == strcmp (t1->otp_key,
+                         t2->otp_key) ) &&
+           (t1->otp_ctr == t2->otp_ctr) &&
+           (t1->otp_algorithm == t2->otp_algorithm) );
+}
+
+
+MHD_RESULT
+TMH_private_post_categories (const struct TMH_RequestHandler *rh,
+                             struct MHD_Connection *connection,
+                             struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 };
+  const char *device_id;
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("otp_device_id",
+                             &device_id),
+    GNUNET_JSON_spec_string ("otp_device_description",
+                             (const char **) &tp.otp_description),
+    TALER_JSON_spec_otp_type ("otp_algorithm",
+                              &tp.otp_algorithm),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_uint64 ("otp_ctr",
+                               &tp.otp_ctr),
+      NULL),
+    TALER_JSON_spec_otp_key ("otp_key",
+                             (const char **) &tp.otp_key),
+    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;
+    }
+  }
+
+  /* finally, interact with DB until no serialization error */
+  for (unsigned int i = 0; i<MAX_RETRIES; i++)
+  {
+    /* Test if a OTP device of this id is known */
+    struct TALER_MERCHANTDB_OtpDeviceDetails etp;
+
+    if (GNUNET_OK !=
+        TMH_db->start (TMH_db->cls,
+                       "/post categories"))
+    {
+      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->select_otp (TMH_db->cls,
+                             mi->settings.id,
+                             device_id,
+                             &etp);
+    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 etp == tp? */
+      {
+        bool eq;
+
+        eq = categories_equal (&tp,
+                               &etp);
+        GNUNET_free (etp.otp_description);
+        GNUNET_free (etp.otp_key);
+        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)
+          : TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_CONFLICT,
+                                        
TALER_EC_MERCHANT_PRIVATE_POST_CATEGORIES_CONFLICT_CATEGORY_EXISTS,
+                                        device_id);
+      }
+    } /* end switch (qs) */
+
+    qs = TMH_db->insert_otp (TMH_db->cls,
+                             mi->settings.id,
+                             device_id,
+                             &tp);
+    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-categories.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-categories.h 
b/src/backend/taler-merchant-httpd_private-post-categories.h
new file mode 100644
index 00000000..a8431059
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-categories.h
@@ -0,0 +1,45 @@
+/*
+  This file is part of TALER
+  (C) 2024 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-categories.h
+ * @brief implementing POST /categories request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_CATEGORIES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_POST_CATEGORIES_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Generate a product category.
+ *
+ * @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_categories (
+  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 89be9a4f..8d1c582c 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -22,6 +22,7 @@ sql_DATA = \
   merchant-0003.sql \
   merchant-0004.sql \
   merchant-0005.sql \
+  merchant-0006.sql \
   drop.sql
 
 BUILT_SOURCES = \
@@ -62,7 +63,7 @@ libtalermerchantdb_la_LIBADD = \
 
 libtalermerchantdb_la_LDFLAGS = \
   $(POSTGRESQL_LDFLAGS) \
-  -version-info 3:0:1 \
+  -version-info 3:1:1 \
   -no-undefined
 
 libtaler_plugin_merchantdb_postgres_la_SOURCES = \
@@ -76,6 +77,11 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
   pg_insert_transfer_details.h pg_insert_transfer_details.c \
   pg_store_wire_fee_by_exchange.h pg_store_wire_fee_by_exchange.c \
   pg_select_open_transfers.h pg_select_open_transfers.c \
+  pg_lookup_categories.h pg_lookup_categories.c \
+  pg_select_category.h pg_select_category.c \
+  pg_update_category.h pg_update_category.c \
+  pg_insert_category.h pg_insert_category.c \
+  pg_delete_category.h pg_delete_category.c \
   pg_lookup_instances.h pg_lookup_instances.c \
   pg_lookup_transfers.h pg_lookup_transfers.c \
   pg_update_transfer_status.h pg_update_transfer_status.c \
diff --git a/src/backenddb/merchant-0006.sql b/src/backenddb/merchant-0006.sql
new file mode 100644
index 00000000..d68927c0
--- /dev/null
+++ b/src/backenddb/merchant-0006.sql
@@ -0,0 +1,55 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2024 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/>
+--
+
+-- Everything in one big transaction
+BEGIN;
+
+-- Check patch versioning is in place.
+SELECT _v.register_patch('merchant-0006', NULL, NULL);
+
+SET search_path TO merchant;
+
+CREATE TABLE IF NOT EXISTS merchant_categories
+  (category_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
+  ,merchant_serial BIGINT NOT NULL
+    REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
+  ,category_name TEXT NOT NULL UNIQUE
+  ,category_name_i18n BYTEA NOT NULL
+  );
+
+COMMENT ON COLUMN merchant_categories.category_name
+  IS 'name of the category';
+COMMENT ON COLUMN merchant_categories.category_name_i18n
+  IS 'JSON with translations of the category name';
+
+CREATE TABLE merchant_product_categories
+  (category_serial BIGINT NOT NULL
+    REFERENCES merchant_categories (category_serial) ON DELETE CASCADE
+  ,product_serial BIGINT NOT NULL
+    REFERENCES merchant_inventory (product_serial) ON DELETE CASCADE);
+CREATE INDEX merchant_categories_by_category
+  ON merchant_categories (category_serial);
+CREATE INDEX merchant_categories_by_product
+  ON merchant_categories (product_serial);
+
+COMMENT ON COLUMN merchant_product_categories.category_serial
+  IS 'Reference to a category the product is part of';
+COMMENT ON COLUMN merchant_product_categories.product_serial
+  IS 'Reference to a product which is in the given category';
+
+
+-- Complete transaction
+COMMIT;
diff --git a/src/backenddb/pg_delete_otp.c b/src/backenddb/pg_delete_category.c
similarity index 70%
copy from src/backenddb/pg_delete_otp.c
copy to src/backenddb/pg_delete_category.c
index 5f011a4b..4a43aa37 100644
--- a/src/backenddb/pg_delete_otp.c
+++ b/src/backenddb/pg_delete_category.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2022 Taler Systems SA
+   Copyright (C) 2024 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
@@ -14,41 +14,41 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_delete_otp.c
- * @brief Implementation of the delete_otp function for Postgres
+ * @file backenddb/pg_delete_category.c
+ * @brief Implementation of the delete_category 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_delete_otp.h"
+#include "pg_delete_category.h"
 #include "pg_helper.h"
 
 
 enum GNUNET_DB_QueryStatus
-TMH_PG_delete_otp (void *cls,
-                   const char *instance_id,
-                   const char *otp_id)
+TMH_PG_delete_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_uint64 (&category_id),
     GNUNET_PQ_query_param_end
   };
 
   check_connection (pg);
   PREPARE (pg,
-           "delete_otp",
+           "delete_category",
            "DELETE"
-           " FROM merchant_otp_devices"
-           " WHERE merchant_otp_devices.merchant_serial="
+           " FROM merchant_categories"
+           " WHERE merchant_serial="
            "     (SELECT merchant_serial "
            "        FROM merchant_instances"
            "        WHERE merchant_id=$1)"
-           "   AND merchant_otp_devices.otp_id=$2");
+           "   AND category_serial=$2");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "delete_otp",
+                                             "delete_category",
                                              params);
 }
diff --git a/src/backenddb/pg_delete_category.h 
b/src/backenddb/pg_delete_category.h
new file mode 100644
index 00000000..8837a6b8
--- /dev/null
+++ b/src/backenddb/pg_delete_category.h
@@ -0,0 +1,43 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_delete_category.h
+ * @brief implementation of the delete_category function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_DELETE_CATEGORY_H
+#define PG_DELETE_CATEGORY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Delete information about a product category.
+ *
+ * @param cls closure
+ * @param instance_id instance to delete category of
+ * @param category_id identifies the category to delete
+ * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ *           if template unknown.
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_delete_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id);
+
+
+#endif
diff --git a/src/backenddb/pg_delete_otp.c b/src/backenddb/pg_delete_otp.c
index 5f011a4b..60f20481 100644
--- a/src/backenddb/pg_delete_otp.c
+++ b/src/backenddb/pg_delete_otp.c
@@ -43,11 +43,11 @@ TMH_PG_delete_otp (void *cls,
            "delete_otp",
            "DELETE"
            " FROM merchant_otp_devices"
-           " WHERE merchant_otp_devices.merchant_serial="
+           " WHERE merchant_serial="
            "     (SELECT merchant_serial "
            "        FROM merchant_instances"
            "        WHERE merchant_id=$1)"
-           "   AND merchant_otp_devices.otp_id=$2");
+           "   AND otp_id=$2");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
                                              "delete_otp",
                                              params);
diff --git a/src/backenddb/pg_insert_category.c 
b/src/backenddb/pg_insert_category.c
new file mode 100644
index 00000000..f820b2c6
--- /dev/null
+++ b/src/backenddb/pg_insert_category.c
@@ -0,0 +1,65 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_category.c
+ * @brief Implementation of the insert_category 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_insert_category.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_category (void *cls,
+                        const char *instance_id,
+                        const char *category_name,
+                        const json_t *category_name_i18n,
+                        uint64_t *category_id)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (category_name),
+    TALER_PQ_query_param_json (category_name_i18n),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("category_serial",
+                                  category_id),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "insert_category",
+           "INSERT INTO merchant_categories"
+           "(category_name"
+           ",category_name_i18n"
+           ")"
+           " SELECT merchant_serial,"
+           " $2, $3"
+           " FROM merchant_instances"
+           " WHERE merchant_id=$1"
+           " RETURNING category_serial");
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "insert_category",
+                                                   params,
+                                                   rs);
+}
diff --git a/src/backenddb/pg_insert_category.h 
b/src/backenddb/pg_insert_category.h
new file mode 100644
index 00000000..8f4bfc99
--- /dev/null
+++ b/src/backenddb/pg_insert_category.h
@@ -0,0 +1,46 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_category.h
+ * @brief implementation of the insert_category function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_CATEGORY_H
+#define PG_INSERT_CATEGORY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Insert new product category.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert OTP device for
+ * @param category_name name of the category
+ * @param category_name_i18n translations of the category name
+ * @param[out] category_id set to the category id on success
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_category (void *cls,
+                        const char *instance_id,
+                        const char *category_name,
+                        const json_t *category_name_i18n,
+                        uint64_t *category_id);
+
+
+#endif
diff --git a/src/backenddb/pg_lookup_categories.c 
b/src/backenddb/pg_lookup_categories.c
new file mode 100644
index 00000000..9aea5477
--- /dev/null
+++ b/src/backenddb/pg_lookup_categories.c
@@ -0,0 +1,148 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_categories.c
+ * @brief Implementation of the lookup_categories 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_categories.h"
+#include "pg_helper.h"
+
+
+/**
+ * Context used for TMH_PG_lookup_categories().
+ */
+struct LookupCategoryContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_CategoriesCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Did database result extraction fail?
+   */
+  bool extract_failed;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about otp_device.
+ *
+ * @param[in,out] cls of type `struct LookupCategoryContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_categories_cb (void *cls,
+                      PGresult *result,
+                      unsigned int num_results)
+{
+  struct LookupCategoryContext *tlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    uint64_t category_id;
+    char *category_name;
+    json_t *category_name_i18n;
+    uint64_t product_count;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint64 ("category_serial",
+                                    &category_id),
+      GNUNET_PQ_result_spec_string ("category_name",
+                                    &category_name),
+      TALER_PQ_result_spec_json ("category_name_i18n",
+                                 &category_name_i18n),
+      GNUNET_PQ_result_spec_uint64 ("product_count",
+                                    &product_count),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      tlc->extract_failed = true;
+      return;
+    }
+    tlc->cb (tlc->cb_cls,
+             category_id,
+             category_name,
+             category_name_i18n,
+             product_count);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_categories (void *cls,
+                          const char *instance_id,
+                          TALER_MERCHANTDB_CategoriesCallback cb,
+                          void *cb_cls)
+{
+
+  struct PostgresClosure *pg = cls;
+  struct LookupCategoryContext tlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    /* Can be overwritten by the lookup_categories_cb */
+    .extract_failed = false,
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "lookup_categories",
+           "SELECT"
+           " mc.category_serial"
+           ",mc.category_name"
+           ",mc.category_name_i18n"
+           ",COALESCE(COUNT(mpc.product_serial),0)"
+           "   AS product_count"
+           " FROM merchant_categories mc"
+           " JOIN merchant_product_categories mpc"
+           " JOIN merchant_instances mi"
+           "   USING (merchant_serial)"
+           " WHERE mi.merchant_id=$1"
+           " GROUP BY mc.category_serial"
+           " ORDER BY mc.category_serial;");
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_categories",
+                                             params,
+                                             &lookup_categories_cb,
+                                             &tlc);
+  /* If there was an error inside lookup_categories_cb, return a hard error. */
+  if (tlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
diff --git a/src/backenddb/pg_lookup_categories.h 
b/src/backenddb/pg_lookup_categories.h
new file mode 100644
index 00000000..500295c0
--- /dev/null
+++ b/src/backenddb/pg_lookup_categories.h
@@ -0,0 +1,43 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_categories.h
+ * @brief implementation of the lookup_categories function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_CATEGORIES_H
+#define PG_LOOKUP_CATEGORIES_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Lookup all of the product categories the given instance has configured.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup OTP devices for
+ * @param cb function to call on all categories found
+ * @param cb_cls closure for @a cb
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_categories (void *cls,
+                          const char *instance_id,
+                          TALER_MERCHANTDB_CategoriesCallback cb,
+                          void *cb_cls);
+
+#endif
diff --git a/src/backenddb/pg_select_category.c 
b/src/backenddb/pg_select_category.c
new file mode 100644
index 00000000..c20d7bb7
--- /dev/null
+++ b/src/backenddb/pg_select_category.c
@@ -0,0 +1,37 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_select_category.c
+ * @brief Implementation of the select_category 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_select_category.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id,
+                        struct TALER_MERCHANTDB_CategoryDetails *cd)
+{
+  GNUNET_break (0); // FIXME
+  return GNUNET_DB_STATUS_HARD_ERROR;
+}
diff --git a/src/backenddb/pg_select_category.h 
b/src/backenddb/pg_select_category.h
new file mode 100644
index 00000000..9eeb14aa
--- /dev/null
+++ b/src/backenddb/pg_select_category.h
@@ -0,0 +1,45 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_select_category.h
+ * @brief implementation of the select_category function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_CATEGORY_H
+#define PG_SELECT_CATEGORY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Lookup details about product category.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup template for
+ * @param category_id category to update
+ * @param[out] cd set to the category details on success, can be NULL
+ *             (in that case we only want to check if the category exists)
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id,
+                        struct TALER_MERCHANTDB_CategoryDetails *cd);
+
+#endif
diff --git a/src/backenddb/pg_delete_otp.c b/src/backenddb/pg_update_category.c
similarity index 54%
copy from src/backenddb/pg_delete_otp.c
copy to src/backenddb/pg_update_category.c
index 5f011a4b..3b07a266 100644
--- a/src/backenddb/pg_delete_otp.c
+++ b/src/backenddb/pg_update_category.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2022 Taler Systems SA
+   Copyright (C) 2024 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
@@ -14,41 +14,46 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_delete_otp.c
- * @brief Implementation of the delete_otp function for Postgres
+ * @file backenddb/pg_update_category.c
+ * @brief Implementation of the update_category 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_delete_otp.h"
+#include "pg_update_category.h"
 #include "pg_helper.h"
 
 
 enum GNUNET_DB_QueryStatus
-TMH_PG_delete_otp (void *cls,
-                   const char *instance_id,
-                   const char *otp_id)
+TMH_PG_update_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id,
+                        const char *category_name,
+                        const json_t *category_name_i18n)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_uint64 (&category_id),
+    GNUNET_PQ_query_param_string (category_name),
+    TALER_PQ_query_param_json (category_name_i18n),
     GNUNET_PQ_query_param_end
   };
 
   check_connection (pg);
   PREPARE (pg,
-           "delete_otp",
-           "DELETE"
-           " FROM merchant_otp_devices"
-           " WHERE merchant_otp_devices.merchant_serial="
-           "     (SELECT merchant_serial "
-           "        FROM merchant_instances"
-           "        WHERE merchant_id=$1)"
-           "   AND merchant_otp_devices.otp_id=$2");
+           "update_category",
+           "UPDATE merchant_categories SET"
+           " category_name=$3"
+           ",category_name_i18n=$4"
+           " WHERE merchant_serial="
+           "   (SELECT merchant_serial"
+           "      FROM merchant_instances"
+           "      WHERE merchant_id=$1)"
+           "   AND category_serial=$2");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "delete_otp",
+                                             "update_category",
                                              params);
 }
diff --git a/src/backenddb/pg_update_category.h 
b/src/backenddb/pg_update_category.h
new file mode 100644
index 00000000..616a714a
--- /dev/null
+++ b/src/backenddb/pg_update_category.h
@@ -0,0 +1,47 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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_update_category.h
+ * @brief implementation of the update_category function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_UPDATE_CATEGORY_H
+#define PG_UPDATE_CATEGORY_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Update descriptions of a product category.
+ *
+ * @param cls closure
+ * @param instance_id instance to update OTP device for
+ * @param category_id category to update
+ * @param category_name name of the category
+ * @param category_name_i18n translations of the category name
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+ *         does not yet exist.
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_update_category (void *cls,
+                        const char *instance_id,
+                        uint64_t category_id,
+                        const char *category_name,
+                        const json_t *category_name_i18n);
+
+#endif
diff --git a/src/backenddb/pg_update_otp.c b/src/backenddb/pg_update_otp.c
index bdcb9624..218ae74e 100644
--- a/src/backenddb/pg_update_otp.c
+++ b/src/backenddb/pg_update_otp.c
@@ -26,17 +26,6 @@
 #include "pg_helper.h"
 
 
-/**
- * Update details about a particular OTP device.
- *
- * @param cls closure
- * @param instance_id instance to update OTP device for
- * @param otp_id OTP device to update
- * @param td update to the OTP device details on success, can be NULL
- *             (in that case we only want to check if the template exists)
- * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
- *         does not yet exist.
- */
 enum GNUNET_DB_QueryStatus
 TMH_PG_update_otp (void *cls,
                    const char *instance_id,
@@ -52,7 +41,7 @@ TMH_PG_update_otp (void *cls,
     GNUNET_PQ_query_param_uint32 (&pos32),
     GNUNET_PQ_query_param_uint64 (&td->otp_ctr),
     (NULL == td->otp_key)
-    ? GNUNET_PQ_query_param_null()
+    ? GNUNET_PQ_query_param_null ()
     : GNUNET_PQ_query_param_string (td->otp_key),
     GNUNET_PQ_query_param_end
   };
@@ -74,5 +63,3 @@ TMH_PG_update_otp (void *cls,
                                              "update_otp",
                                              params);
 }
-
-
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 4a03dbfe..ede43100 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -44,6 +44,11 @@
 #include "pg_lookup_instances.h"
 #include "pg_lookup_transfers.h"
 #include "pg_lookup_pending_deposits.h"
+#include "pg_lookup_categories.h"
+#include "pg_select_category.h"
+#include "pg_update_category.h"
+#include "pg_insert_category.h"
+#include "pg_delete_category.h"
 #include "pg_update_wirewatch_progress.h"
 #include "pg_select_wirewatch_accounts.h"
 #include "pg_select_open_transfers.h"
@@ -564,6 +569,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_insert_pending_webhook;
   plugin->update_pending_webhook
     = &TMH_PG_update_pending_webhook;
+  plugin->lookup_categories
+    = &TMH_PG_lookup_categories;
+  plugin->select_category
+    = &TMH_PG_select_category;
+  plugin->update_category
+    = &TMH_PG_update_category;
+  plugin->insert_category
+    = &TMH_PG_insert_category;
+  plugin->delete_category
+    = &TMH_PG_delete_category;
   plugin->delete_exchange_accounts
     = &TMH_PG_delete_exchange_accounts;
   plugin->select_accounts_by_exchange
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 12b13e33..d06ab76c 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2023 Taler Systems SA
+  Copyright (C) 2014-2024 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -440,6 +440,75 @@ struct TALER_MERCHANTDB_OtpDeviceDetails
 };
 
 
+/**
+ * Typically called by `lookup_categories`.
+ *
+ * @param cls closure
+ * @param category_id ID of the category
+ * @param category_name name of the category
+ * @param category_name_i18n translations of the @a category_name
+ * @param product_count number of products in the category
+ */
+typedef void
+(*TALER_MERCHANTDB_CategoriesCallback)(
+  void *cls,
+  uint64_t category_id,
+  const char *category_name,
+  const json_t *category_name_i18n,
+  uint64_t product_count);
+
+
+/**
+ * Details about a product category.
+ */
+struct TALER_MERCHANTDB_ProductSummary
+{
+  /**
+   * ID of the product.
+   */
+  char *product_id;
+
+  /**
+   * Description for the product.
+   */
+  char *description;
+
+  /**
+   * Translation of the @e description.
+   */
+  json_t *description_i18n;
+
+};
+
+/**
+ * Details about a product category.
+ */
+struct TALER_MERCHANTDB_CategoryDetails
+{
+
+  /**
+   * Name of the category.
+   */
+  char *category_name;
+
+  /**
+   * Translations of the name of the category.
+   */
+  json_t *category_name_i18n;
+
+  /**
+   * Products in the category.
+   */
+  struct TALER_MERCHANTDB_ProductSummary *products;
+
+  /**
+   * Length of the @e products array.
+   */
+  unsigned int num_products;
+
+};
+
+
 /**
  * Typically called by `lookup_webhooks`.
  *
@@ -2875,6 +2944,24 @@ struct TALER_MERCHANTDB_Plugin
                      const struct TALER_MERCHANTDB_TemplateDetails *td);
 
 
+  /**
+   * Update details about a particular template.
+   *
+   * @param cls closure
+   * @param instance_id instance to update template for
+   * @param template_id template to update
+   * @param td update to the template details on success, can be NULL
+   *             (in that case we only want to check if the template exists)
+   * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+   *         does not yet exist.
+   */
+  enum GNUNET_DB_QueryStatus
+  (*update_template)(void *cls,
+                     const char *instance_id,
+                     const char *template_id,
+                     const struct TALER_MERCHANTDB_TemplateDetails *td);
+
+
   /**
    * Delete information about an OTP device.
    *
@@ -2971,21 +3058,86 @@ struct TALER_MERCHANTDB_Plugin
 
 
   /**
-   * Update details about a particular template.
+   * Delete information about a product category.
    *
    * @param cls closure
-   * @param instance_id instance to update template for
-   * @param template_id template to update
-   * @param td update to the template details on success, can be NULL
-   *             (in that case we only want to check if the template exists)
+   * @param instance_id instance to delete category of
+   * @param category_id identifies the category to delete
+   * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+   *           if template unknown.
+   */
+  enum GNUNET_DB_QueryStatus
+  (*delete_category)(void *cls,
+                     const char *instance_id,
+                     uint64_t category_id);
+
+  /**
+   * Insert new product category.
+   *
+   * @param cls closure
+   * @param instance_id instance to insert OTP device for
+   * @param category_name name of the category
+   * @param category_name_i18n translations of the category name
+   * @param[out] category_id set to the category id on success
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*insert_category)(void *cls,
+                     const char *instance_id,
+                     const char *category_name,
+                     const json_t *category_name_i18n,
+                     uint64_t *category_id);
+
+
+  /**
+   * Update descriptions of a product category.
+   *
+   * @param cls closure
+   * @param instance_id instance to update OTP device for
+   * @param category_id category to update
+   * @param category_name name of the category
+   * @param category_name_i18n translations of the category name
    * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
    *         does not yet exist.
    */
   enum GNUNET_DB_QueryStatus
-  (*update_template)(void *cls,
+  (*update_category)(void *cls,
                      const char *instance_id,
-                     const char *template_id,
-                     const struct TALER_MERCHANTDB_TemplateDetails *td);
+                     uint64_t category_id,
+                     const char *category_name,
+                     const json_t *category_name_i18n);
+
+  /**
+   * Lookup all of the product categories the given instance has configured.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup OTP devices for
+   * @param cb function to call on all categories found
+   * @param cb_cls closure for @a cb
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_categories)(void *cls,
+                       const char *instance_id,
+                       TALER_MERCHANTDB_CategoriesCallback cb,
+                       void *cb_cls);
+
+
+  /**
+   * Lookup details about product category.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup template for
+   * @param category_id category to update
+   * @param[out] cd set to the category details on success, can be NULL
+   *             (in that case we only want to check if the category exists)
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_category)(void *cls,
+                     const char *instance_id,
+                     uint64_t category_id,
+                     struct TALER_MERCHANTDB_CategoryDetails *cd);
 
 
   /**

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]