gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: backenddb test ok


From: gnunet
Subject: [taler-merchant] branch master updated: backenddb test ok
Date: Wed, 11 Jan 2023 16:42:01 +0100

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

priscilla-huang pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new f05235f5 backenddb test ok
f05235f5 is described below

commit f05235f55bddb98989812c35d96504c5b7e2a918
Author: priscilla <priscilla.huang@efrei.net>
AuthorDate: Wed Jan 11 10:41:24 2023 -0500

    backenddb test ok
---
 src/backenddb/merchant-0004.sql               |   8 +-
 src/backenddb/plugin_merchantdb_postgres.c    | 526 ++++++++++++++++++++++-
 src/backenddb/test_merchantdb.c               | 587 +++++++++++++++++++++++++-
 src/include/taler_merchantdb_plugin.h         | 211 ++++++++-
 src/testing/#testing_api_cmd_post_products.c# | 332 ---------------
 5 files changed, 1307 insertions(+), 357 deletions(-)

diff --git a/src/backenddb/merchant-0004.sql b/src/backenddb/merchant-0004.sql
index 93a03dae..55cfa2fc 100644
--- a/src/backenddb/merchant-0004.sql
+++ b/src/backenddb/merchant-0004.sql
@@ -46,7 +46,7 @@ COMMENT ON COLUMN merchant_template.image
   IS 'NOT NULL, but can be 0 bytes; must contain an ImageDataUrl';
 COMMENT ON COLUMN merchant_template.template_contract
   IS 'The template contract will contains some additional information.';
-  
+
 
 CREATE TABLE IF NOT EXISTS merchant_webhook
   (webhook_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
@@ -73,15 +73,15 @@ COMMENT ON COLUMN merchant_webhook.header_template
 COMMENT ON COLUMN merchant_webhook.body_template
   IS 'Template for the body of the webhook, to be modified based on trigger 
data';
 
-/*
+
 CREATE TABLE IF NOT EXISTS merchant_pending_webhooks
   (webhook_pending_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
   ,merchant_serial BIGINT NOT NULL
     REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
   ,webhook_serial BIGINT NOT NULL
     REFERENCES merchant_webhook (webhook_serial) ON DELETE CASCADE
-  ,retries INT4 NOT NULL DEFAULT(0)
   ,next_attempt INT8 NOT NULL DEFAULT(0)
+  ,retries INT4 NOT NULL DEFAULT(0)
   ,url VARCHAR NOT NULL
   ,http_method VARCHAR NOT NULL
   ,header VARCHAR
@@ -104,7 +104,7 @@ COMMENT ON COLUMN merchant_pending_webhooks.header
   IS 'Header of the webhook';
 COMMENT ON COLUMN merchant_pending_webhooks.body
   IS 'Body of the webhook';
-*/
+
 
 COMMIT;
 
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index b52f058b..e9ddabe7 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -7113,7 +7113,7 @@ postgres_delete_webhook (void *cls,
  * Insert details about a particular webhook.
  *
  * @param cls closure
- * @param instance_id instance to insert template for
+ * @param instance_id instance to insert webhook for
  * @param webhook_id webhook identifier of webhook to insert
  * @param wb the webhook details to insert
  * @return database result code
@@ -7149,7 +7149,7 @@ postgres_insert_webhook (void *cls,
  *
  * @param cls closure
  * @param instance_id instance to update template for
- * @param webhook_id template to update
+ * @param webhook_id webhook to update
  * @param wb update to the webhook details on success, can be NULL
  *             (in that case we only want to check if the webhook exists)
  * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
webhook
@@ -7348,6 +7348,432 @@ postgres_lookup_webhook (void *cls,
 }
 
 
+/**
+ * Context used for postgres_lookup_webhook().
+ */
+struct LookupWebhookDetailContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_WebhookDetailCallback 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 webhook.
+ *
+ * @param[in,out] cls of type `struct LookupPendingWebhookContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_webhook_by_event_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
+{
+  struct LookupWebhookDetailContext *wlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    uint64_t webhook_serial;
+    char *event_type;
+    char *url;
+    char *http_method;
+    char *header_template;
+    char *body_template;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint64 ("webhook_serial",
+                                    &webhook_serial),
+      GNUNET_PQ_result_spec_string ("event_type",
+                                    &event_type),
+      GNUNET_PQ_result_spec_string ("url",
+                                    &url),
+      GNUNET_PQ_result_spec_string ("http_method",
+                                    &http_method),
+      GNUNET_PQ_result_spec_string ("header_template",
+                                    &header_template),
+      GNUNET_PQ_result_spec_string ("body_template",
+                                    &body_template),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      wlc->extract_failed = true;
+      return;
+    }
+    wlc->cb (wlc->cb_cls,
+             webhook_serial,
+             event_type,
+             url,
+             http_method,
+             header_template,
+             body_template);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+/**
+   * Lookup webhook by event
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup webhook for
+   * @param event_type event that we need to put in the pending webhook
+   * @param[out] cb set to the webhook details on success
+   * @param cb_cls callback closure
+   * @return database result code
+   */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_webhook_by_event(void *cls,
+                                  const char *instance_id,
+                                  const char *event_type,
+                                  TALER_MERCHANTDB_WebhookDetailCallback cb,
+                                  void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupWebhookDetailContext wlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .extract_failed = false,
+  };
+
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (event_type),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_webhook_by_event",
+                                             params,
+                                             &lookup_webhook_by_event_cb,
+                                             &wlc);
+
+  if (wlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+/**
+ * Insert webhook in the pending webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert webhook for
+ * @param webhook_serial webhook to insert in the pending webhook
+ * @param url to make the request to
+ * @param http_method for the webhook
+ * @param header of the webhook
+ * @param body of the webhook
+ * @return database result code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_insert_pending_webhook(void *cls,
+                                const char *instance_id,
+                                uint64_t webhook_serial,
+                                const char *url,
+                                const char *http_method,
+                                const char *header,
+                                const char *body)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_uint64 (&webhook_serial),
+    GNUNET_PQ_query_param_string (url),
+    GNUNET_PQ_query_param_string (http_method),
+    NULL == header
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_string (header),
+    NULL == body
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_string (body),
+    GNUNET_PQ_query_param_end
+  };
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_pending_webhook",
+                                             params);
+}
+
+/**
+ * Context used for postgres_lookup_future_webhook().
+ */
+struct LookupPendingWebhookContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_PendingWebhooksCallback 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 webhook.
+ *
+ * @param[in,out] cls of type `struct LookupPendingWebhookContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_pending_webhooks_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
+{
+  struct LookupPendingWebhookContext *pwlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    uint64_t webhook_serial;
+    struct GNUNET_TIME_Absolute next_attempt;
+    uint32_t retries;
+    char *url;
+    char *http_method;
+    char *header;
+    char *body;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint64 ("webhook_serial",
+                                    &webhook_serial),
+      GNUNET_PQ_result_spec_absolute_time ("next_attempt",
+                                           &next_attempt),
+      GNUNET_PQ_result_spec_uint32 ("retries",
+                                    &retries),
+      GNUNET_PQ_result_spec_string ("url",
+                                    &url),
+      GNUNET_PQ_result_spec_string ("http_method",
+                                    &http_method),
+      GNUNET_PQ_result_spec_string ("header",
+                                    &header),
+      GNUNET_PQ_result_spec_string ("body",
+                                    &body),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      pwlc->extract_failed = true;
+      return;
+    }
+    pwlc->cb (pwlc->cb_cls,
+              webhook_serial,
+              next_attempt,
+              retries,
+              url,
+              http_method,
+              header,
+              body);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+/**
+ * Lookup the webhook that need to be send in priority.
+ * send.
+ *
+ * @param cls closure
+ * @param cb pending webhook callback
+ * @param cb_cls callback closure
+ */
+// WHERE next_attempt <= now ORDER BY next_attempt ASC
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_pending_webhook(void *cls,
+                                TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                                void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupPendingWebhookContext pwlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .extract_failed = false,
+  };
+  struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+  struct GNUNET_PQ_QueryParam params_null[] = {
+    GNUNET_PQ_query_param_absolute_time (&now),
+    GNUNET_PQ_query_param_end
+  };
+
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_pending_webhook",
+                                             params_null,
+                                             &lookup_pending_webhooks_cb,
+                                             &pwlc);
+
+  if (pwlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
+/**
+ * Lookup future webhook in the pending webhook that need to be send.
+ * With that we can know how long the system can 'sleep'.
+ *
+ * @param cls closure
+ * @param cb pending webhook callback
+ * @param cb_cls callback closure
+ */
+// ORDER BY next_attempt ASC LIMIT 1
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_future_webhook(void *cls,
+                               TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                               void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupPendingWebhookContext pwlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .extract_failed = false,
+  };
+  struct GNUNET_PQ_QueryParam params_null[] = {
+    GNUNET_PQ_query_param_end
+  };
+
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_future_webhook",
+                                             params_null,
+                                             &lookup_pending_webhooks_cb,
+                                             &pwlc);
+
+  if (pwlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+ /**
+   * Lookup all the webhooks in the pending webhook.
+   * Use by the administrator
+   *
+   * @param cls closure
+   * @param instance_id to lookup webhooks for this instance particularly
+   * @param min_row to see the list of the pending webhook that it is started 
with this minimum row.
+   * @param max_results to see the list of the pending webhook that it is end 
with this max results.
+   * @param cb pending webhook callback
+   * @param cb_cls callback closure
+   */
+  // WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial 
ASC LIMIT max_results
+  static enum GNUNET_DB_QueryStatus
+  postgres_lookup_all_webhooks(void *cls,
+                               const char *instance_id,
+                               uint64_t min_row,
+                               uint32_t max_results,
+                               TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                               void *cb_cls)
+  {
+  struct PostgresClosure *pg = cls;
+  struct LookupPendingWebhookContext pwlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .extract_failed = false,
+  };
+  uint64_t max_results64 = max_results;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_uint64 (&min_row),
+    GNUNET_PQ_query_param_uint64  (&max_results64),
+    GNUNET_PQ_query_param_end
+  };
+
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_all_webhooks",
+                                             params,
+                                             &lookup_pending_webhooks_cb,
+                                             &pwlc);
+
+  if (pwlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+/**
+ * Update the pending webhook. It is use if the webhook can't be send.
+ *
+ * @param cls closure
+ * @param webhook_serial webhook that need to be update
+ * @param next_attempt when we should make the next request to the webhook
+ * @return database result code
+ */
+  static enum GNUNET_DB_QueryStatus
+  postgres_update_pending_webhook(void *cls,
+                                  uint64_t webhook_serial,
+                                  struct GNUNET_TIME_Absolute next_attempt)
+  // maybe add: http status of failure?
+{
+    struct PostgresClosure *pg = cls;
+    struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&webhook_serial),
+    GNUNET_PQ_query_param_absolute_time (&next_attempt),
+    GNUNET_PQ_query_param_end
+
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "update_pending_webhook",
+                                             params);
+}
+
+/**
+ * Delete a webhook in the pending webhook if it is successfull
+ *
+ * @param cls closure
+ * @param webhook_serial webhook that need to be delete in the pending webhook
+ * @return database result code
+ */
+  static enum GNUNET_DB_QueryStatus
+  postgres_delete_pending_webhook(void *cls,
+                                  uint64_t webhook_serial)
+  {
+    struct PostgresClosure *pg = cls;
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_uint64 (&webhook_serial),
+      GNUNET_PQ_query_param_end
+    };
+    check_connection (pg);
+    return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                               "delete_pending_webhook",
+                                               params);
+}
+
+
 /**
  * Establish connection to the database.
  *
@@ -9906,6 +10332,95 @@ postgres_connect (void *cls)
                             "      FROM merchant_instances"
                             "      WHERE merchant_id=$1)"
                             "   AND webhook_id=$2"),
+    /* for postgres_lookup_webhook_by_event() */
+    GNUNET_PQ_make_prepare ("lookup_webhook_by_event",
+                            "SELECT"
+                            " webhook_serial"
+                            ",event_type"
+                            ",url"
+                            ",http_method"
+                            ",header_template"
+                            ",body_template"
+                            " FROM merchant_webhook"
+                            " JOIN merchant_instances"
+                            "   USING (merchant_serial)"
+                            " WHERE merchant_instances.merchant_id=$1"
+                            "  AND event_type=$2"),
+    /* for postgres_delete_pending_webhook() */
+    GNUNET_PQ_make_prepare ("delete_pending_webhook",
+                            "DELETE"
+                            " FROM merchant_pending_webhooks"
+                            " WHERE merchant_pending_webhooks.webhook_serial="
+                            "   (SELECT webhook_serial "
+                            "      FROM merchant_webhook"
+                            "      WHERE webhook_serial=$1)"),
+    /* for postgres_insert_pending_webhook() */
+    GNUNET_PQ_make_prepare ("insert_pending_webhook",
+                            "INSERT INTO merchant_pending_webhooks"
+                            "(merchant_serial"
+                            ",webhook_serial"
+                            ",url"
+                            ",http_method"
+                            ",header"
+                            ",body"
+                            ")"
+                            " SELECT mi.merchant_serial,"
+                            " $2, $3, $4, $5, $6"
+                            " FROM merchant_instances mi"
+                            " WHERE mi.merchant_id=$1"),
+    /* for postgres_update_pending_webhook() */
+    GNUNET_PQ_make_prepare ("update_pending_webhook",
+                            "UPDATE merchant_pending_webhooks SET"
+                            " retries=retries+1"
+                            ",next_attempt=$2"
+                            " WHERE webhook_serial="
+                            "   (SELECT webhook_serial"
+                            "     FROM merchant_webhook"
+                            "     WHERE webhook_serial=$1)"),
+    /* for postgres_lookup_pending_webhook() */
+    GNUNET_PQ_make_prepare ("lookup_pending_webhook",
+                            "SELECT"
+                            " webhook_serial"
+                            ",next_attempt"
+                            ",retries"
+                            ",url"
+                            ",http_method"
+                            ",header"
+                            ",body"
+                            " FROM merchant_pending_webhooks"
+                            " WHERE next_attempt <= $1"
+                            "   ORDER BY next_attempt ASC"
+                            ),
+    /* for postgres_lookup_future_webhook() */
+    GNUNET_PQ_make_prepare ("lookup_future_webhook",
+                            "SELECT"
+                            " webhook_serial"
+                            ",next_attempt"
+                            ",retries"
+                            ",url"
+                            ",http_method"
+                            ",header"
+                            ",body"
+                            " FROM merchant_pending_webhooks"
+                            " ORDER BY next_attempt ASC LIMIT 1"
+                            ),
+    /* for postgres_lookup_all_webhooks() */
+    GNUNET_PQ_make_prepare ("lookup_all_webhooks",
+                            " SELECT"
+                            " webhook_serial"
+                            ",next_attempt"
+                            ",retries"
+                            ",url"
+                            ",http_method"
+                            ",header"
+                            ",body"
+                            " FROM merchant_pending_webhooks"
+                            " JOIN merchant_instances"
+                            "   USING (merchant_serial)"
+                            " WHERE merchant_instances.merchant_id=$1"
+                            " AND webhook_serial > $2"
+                            "  ORDER BY webhook_serial"
+                            "   ASC LIMIT $3"),
     GNUNET_PQ_PREPARED_STATEMENT_END
   };
   struct GNUNET_PQ_ExecuteStatement es[] = {
@@ -10065,6 +10580,13 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->delete_webhook = &postgres_delete_webhook;
   plugin->insert_webhook = &postgres_insert_webhook;
   plugin->update_webhook = &postgres_update_webhook;
+  plugin->lookup_webhook_by_event = &postgres_lookup_webhook_by_event;
+  plugin->lookup_all_webhooks = &postgres_lookup_all_webhooks;
+  plugin->lookup_future_webhook = &postgres_lookup_future_webhook;
+  plugin->lookup_pending_webhook = &postgres_lookup_pending_webhook;
+  plugin->delete_pending_webhook = &postgres_delete_pending_webhook;
+  plugin->insert_pending_webhook = &postgres_insert_pending_webhook;
+  plugin->update_pending_webhook = &postgres_update_pending_webhook;
   return plugin;
 }
 
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 589d14a4..e24e4c04 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -818,7 +818,8 @@ check_products_equal (const struct 
TALER_MERCHANTDB_ProductDetails *a,
       (GNUNET_TIME_timestamp_cmp (a->next_restock,
                                   !=,
                                   b->next_restock)))
-    return 1;
+
+      return 1;
   return 0;
 }
 
@@ -6865,8 +6866,7 @@ make_template (const char *id,
 {
   template->id = id;
   template->template.template_description = "This is a test template";
-  template->template.image = GNUNET_strdup ("");
-  GNUNET_assert (NULL != template->template.image);
+  template->template.image = NULL;
   template->template.template_contract = json_array ();
   GNUNET_assert (NULL != template->template.template_contract);
 }
@@ -6898,8 +6898,9 @@ check_templates_equal (const struct 
TALER_MERCHANTDB_TemplateDetails *a,
 {
   if ((0 != strcmp (a->template_description,
                     b->template_description)) ||
-      (0 != strcmp (a->image,
-                    b->image)) ||
+      ( (NULL == a->image) && (NULL != b->image)) ||
+      ( (NULL != a->image) && (NULL == b->image)) ||
+      ( (NULL != a->image) && (0 != strcmp (a->image, b->image))) ||
       (1 != json_equal (a->template_contract,
                         b->template_contract)))
     return 1;
@@ -7034,8 +7035,10 @@ lookup_templates_cb (void *cls,
   cmp->results_length += 1;
   for (unsigned int i = 0; cmp->templates_to_cmp_length > i; ++i)
   {
-    if (0 == strcmp (cmp->templates_to_cmp[i].id,
-                     template_id))
+    if ((0 == strcmp (cmp->templates_to_cmp[i].id,
+                     template_id)) &&
+        (0 == strcmp (cmp->templates_to_cmp[i].template.template_description,
+                      template_description)) )
       cmp->results_matching[i] += 1;
   }
 }
@@ -7269,6 +7272,7 @@ test_templates (void)
  */
 struct WebhookData
 {
+
   /**
    * The identifier of the webhook.
    */
@@ -7293,7 +7297,7 @@ make_webhook (const char *id,
 {
   webhook->id = id;
   webhook->webhook.event_type = "Paid";
-  webhook->webhook.url = "https://example.com";;
+  webhook->webhook.url = "https://exampletest.com";;
   webhook->webhook.http_method = "POST";
   webhook->webhook.header_template = "Authorization:XYJAORKJEO";
   webhook->webhook.body_template = "$Amount";
@@ -7453,8 +7457,10 @@ lookup_webhooks_cb (void *cls,
   cmp->results_length += 1;
   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
   {
-    if (0 == strcmp (cmp->webhooks_to_cmp[i].id,
-                     webhook_id))
+    if ((0 == strcmp (cmp->webhooks_to_cmp[i].id,
+                      webhook_id)) &&
+        (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
+                      event_type)) )
       cmp->results_matching[i] += 1;
   }
 }
@@ -7508,6 +7514,92 @@ test_lookup_webhooks (const struct InstanceData 
*instance,
   return 0;
 }
 
+/**
+ * Function called after calling @e test_lookup_webhooks
+ *
+ * @param cls a pointer to the lookup closure.
+ * @param webhook_id the identifier of the webhook found.
+ */
+static void
+lookup_webhook_by_event_cb (void *cls,
+                            uint64_t webhook_serial,
+                            const char *event_type,
+                            const char *url,
+                            const char *http_method,
+                            const char *header_template,
+                            const char *body_template)
+{
+  struct TestLookupWebhooks_Closure *cmp = cls;
+  if (NULL == cmp)
+    return;
+  cmp->results_length += 1;
+  for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
+  {
+    if ((0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
+                      event_type)) &&
+        (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.url,
+                     url)) &&
+        (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.http_method,
+                     http_method)) &&
+        (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.header_template,
+                     header_template)) &&
+        (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.body_template,
+                      body_template)) )
+      cmp->results_matching[i] += 1;
+  }
+  }
+
+/**
+ * Tests looking up webhooks by event for an instance.
+ *
+ * @param instance the instance to query from.
+ * @param webhooks_length the number of webhooks we are expecting.
+ * @param webhooks the list of webhooks that we expect to be found.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_webhook_by_event (const struct InstanceData *instance,
+                              unsigned int webhooks_length,
+                              const struct WebhookData *webhooks)
+{
+  unsigned int results_matching[webhooks_length];
+  struct TestLookupWebhooks_Closure cls = {
+    .webhooks_to_cmp_length = webhooks_length,
+    .webhooks_to_cmp = webhooks,
+    .results_matching = results_matching,
+    .results_length = 0
+  };
+  memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
+  if (0 > plugin->lookup_webhook_by_event (plugin->cls,
+                                           instance->instance.id,
+                                           webhooks->webhook.event_type,
+                                           &lookup_webhook_by_event_cb,
+                                           &cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhooks by event failed\n");
+    return 1;
+  }
+
+  if (webhooks_length != cls.results_length)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup webhooks by event failed: incorrect number of 
results\n");
+    return 1;
+  }
+  for (unsigned int i = 0; webhooks_length > i; ++i)
+  {
+    if (1 != cls.results_matching[i])
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Lookup webhooks by event failed: mismatched data\n");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+
 
 /**
  * Tests deleting a webhook.
@@ -7544,7 +7636,7 @@ struct TestWebhooks_Closure
   /**
    * The array of webhooks.
    */
-  struct WebhookData webhooks[2];
+  struct WebhookData webhooks[3];
 };
 
 
@@ -7567,6 +7659,18 @@ pre_test_webhooks (struct TestWebhooks_Closure *cls)
   make_webhook ("test_webhooks_wb_1",
                 &cls->webhooks[1]);
   cls->webhooks[1].webhook.event_type = "Test paid";
+  cls->webhooks[1].webhook.url = "https://example.com";;
+  cls->webhooks[1].webhook.http_method = "POST";
+  cls->webhooks[1].webhook.header_template = "Authorization:1XYJAOR493O";
+  cls->webhooks[1].webhook.body_template = "$Amount";
+
+  make_webhook ("test_webhooks_wb_2",
+                &cls->webhooks[2]);
+  cls->webhooks[2].webhook.event_type = "Test paid";
+  cls->webhooks[2].webhook.url = "https://examplerefund.com";;
+  cls->webhooks[2].webhook.http_method = "POST";
+  cls->webhooks[2].webhook.header_template = "Authorization:XY6ORK52JEO";
+  cls->webhooks[2].webhook.body_template = "$Amount";
 }
 
 
@@ -7625,7 +7729,7 @@ run_test_webhooks (struct TestWebhooks_Closure *cls)
   cls->webhooks[0].webhook.event_type =
     "Test paid";
   cls->webhooks[0].webhook.url =
-    "https://example.com";;
+    "example.com";
   cls->webhooks[0].webhook.http_method =
     "POST";
   cls->webhooks[0].webhook.header_template =
@@ -7648,6 +7752,12 @@ run_test_webhooks (struct TestWebhooks_Closure *cls)
   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
                                           2,
                                           cls->webhooks));
+  TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
+                                                  2,
+                                                  cls->webhooks));
+  TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
+                                         &cls->webhooks[2],
+                                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
 
   /* Test webhook deletion */
   TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
@@ -7657,9 +7767,13 @@ run_test_webhooks (struct TestWebhooks_Closure *cls)
   TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
                                          &cls->webhooks[1],
                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  cls->webhooks[1] = cls->webhooks[2];
   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
-                                          1,
+                                          2,
                                           cls->webhooks));
+  TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
+                                                  2,
+                                                  cls->webhooks));
   return 0;
 }
 
@@ -7680,6 +7794,452 @@ test_webhooks (void)
 }
 
 
+
+/* *********** Pending Webhooks ********** */
+
+/**
+ * A container for data relevant to a pending webhook.
+ */
+struct PendingWebhookData
+{
+  /**
+   * Reference to the configured webhook template.
+   */
+   uint64_t webhook_serial;
+
+  /**
+   * The details of the pending webhook.
+   */
+  struct TALER_MERCHANTDB_PendingWebhookDetails pwebhook;
+};
+
+
+
+
+/**
+ * Creates a pending webhook for testing with.
+ *
+ * @param serial reference to the configured webhook template.
+ * @param pwebhook the pending webhook data to fill.
+ */
+static void
+make_pending_webhook (uint64_t webhook_serial,
+                      struct PendingWebhookData *pwebhook)
+{
+  pwebhook->webhook_serial = webhook_serial;
+  pwebhook->pwebhook.url = "https://exampletest.com";;
+  pwebhook->pwebhook.http_method = "POST";
+  pwebhook->pwebhook.header = "Authorization:XYJAORKJEO";
+  pwebhook->pwebhook.body = "$Amount";
+}
+
+
+
+/**
+ * Tests inserting pending webhook data into the database.
+ *
+ * @param instance the instance to insert the pending webhook for.
+ * @param pending webhook the pending webhook data to insert.
+ * @param expected_result the result we expect the db to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_insert_pending_webhook (const struct InstanceData *instance,
+                             struct PendingWebhookData *pwebhook,
+                             enum GNUNET_DB_QueryStatus expected_result)
+{
+
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->insert_pending_webhook (plugin->cls,
+                                                         instance->instance.id,
+                                                         
pwebhook->webhook_serial,
+                                                         
pwebhook->pwebhook.url,
+                                                         
pwebhook->pwebhook.http_method,
+                                                         
pwebhook->pwebhook.header,
+                                                         
pwebhook->pwebhook.body),
+                         "Insert pending webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Tests updating pending webhook data in the database.
+ *
+ * @param instance the instance to update the pending webhook for.
+ * @param pending webhook the pending webhook data to update.
+ * @param expected_result the result we expect the db to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_update_pending_webhook (const struct InstanceData *instance,
+                             struct PendingWebhookData *pwebhook,
+                             enum GNUNET_DB_QueryStatus expected_result)
+{
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->update_pending_webhook (plugin->cls,
+                                                         
pwebhook->webhook_serial,
+                                                         
pwebhook->pwebhook.next_attempt),
+                         "Update pending webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Closure for testing pending webhook lookup
+ */
+struct TestLookupPendingWebhooks_Closure
+{
+  /**
+   * Number of webhook serial to compare to
+   */
+  unsigned int webhooks_to_cmp_length;
+
+  /**
+   * Pointer to array of webhook serials
+   */
+  const struct PendingWebhookData *webhooks_to_cmp;
+
+  /**
+   * Pointer to array of number of matches for each pending webhook
+   */
+  unsigned int *results_matching;
+
+  /**
+   * Total number of results returned
+   */
+  unsigned int results_length;
+};
+
+/**
+ * Function called after calling @e test_lookup_all_webhook,
+ * test_lookup_future_webhook and test_lookup_pending_webhook
+ *
+ * @param cls a pointer to the lookup closure.
+ * @param webhook_serial reference to the configured webhook template.
+ */
+static void
+lookup_pending_webhooks_cb (void *cls,
+                            uint64_t webhook_serial,
+                            struct GNUNET_TIME_Absolute next_attempt,
+                            uint32_t retries,
+                            const char *url,
+                            const char *http_method,
+                            const char *header,
+                            const char *body)
+{
+  struct TestLookupPendingWebhooks_Closure *cmp = cls;
+  if (NULL == cmp)
+    return;
+  cmp->results_length += 1;
+  for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
+  {
+    if ((cmp->webhooks_to_cmp[i].webhook_serial != webhook_serial) &&
+         (GNUNET_TIME_absolute_cmp 
(cmp->webhooks_to_cmp[i].pwebhook.next_attempt,
+                                    !=,
+                                    next_attempt)))
+      cmp->results_matching[i] += 1;
+  }
+}
+
+
+/**
+ * Tests looking up the pending webhook for an instance.
+ *
+ * @param instance the instance to query from.
+ * @param pwebhooks_length the number of pending webhook we are expecting.
+ * @param pwebhooks the list of pending webhooks that we expect to be found.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_pending_webhook (const struct InstanceData *instance,
+                             unsigned int pwebhooks_length,
+                             const struct PendingWebhookData *pwebhooks)
+{
+  unsigned int results_matching[pwebhooks_length];
+  struct TestLookupPendingWebhooks_Closure cls = {
+    .webhooks_to_cmp_length = pwebhooks_length,
+    .webhooks_to_cmp = pwebhooks,
+    .results_matching = results_matching,
+    .results_length = 0
+  };
+  memset (results_matching, 0, sizeof (unsigned int) * pwebhooks_length);
+  if (0 > plugin->lookup_pending_webhook (plugin->cls,
+                                          &lookup_pending_webhooks_cb,
+                                          &cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup pending webhook failed\n");
+    return 1;
+  }
+  if (pwebhooks_length != cls.results_length)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup pending webhook failed: incorrect number of 
results\n");
+    return 1;
+  }
+  for (unsigned int i = 0; pwebhooks_length > i; ++i)
+  {
+    if (1 != cls.results_matching[i])
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Lookup pending webhook failed: mismatched data\n");
+      return 1;
+    }
+  }
+  return 0;
+  }
+
+/**
+ * Tests looking up the future webhook to send for an instance.
+ *
+ * @param instance the instance to query from.
+ * @param pwebhooks_length the number of pending webhook we are expecting.
+ * @param pwebhooks the list of pending webhooks that we expect to be found.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_future_webhook (const struct InstanceData *instance,
+                             unsigned int pwebhooks_length,
+                             const struct PendingWebhookData *pwebhooks)
+{
+  unsigned int results_matching[pwebhooks_length];
+  struct TestLookupPendingWebhooks_Closure cls = {
+    .webhooks_to_cmp_length = pwebhooks_length,
+    .webhooks_to_cmp = pwebhooks,
+    .results_matching = results_matching,
+    .results_length = 0
+  };
+  memset (results_matching, 0, sizeof (unsigned int) * pwebhooks_length);
+  if (0 > plugin->lookup_future_webhook (plugin->cls,
+                                         &lookup_pending_webhooks_cb,
+                                         &cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup future webhook failed\n");
+    return 1;
+  }
+  if (pwebhooks_length != cls.results_length)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup future webhook failed: incorrect number of results\n");
+    return 1;
+  }
+  for (unsigned int i = 0; pwebhooks_length > i; ++i)
+  {
+    if (1 != cls.results_matching[i])
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Lookup future webhook failed: mismatched data\n");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Tests looking up all the pending webhook for an instance.
+ *
+ * @param instance the instance to query from.
+ * @param pwebhooks_length the number of pending webhook we are expecting.
+ * @param pwebhooks the list of pending webhooks that we expect to be found.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_lookup_all_webhooks (const struct InstanceData *instance,
+                          unsigned int pwebhooks_length,
+                          const struct PendingWebhookData *pwebhooks)
+{
+  uint32_t max_results=pwebhooks_length;
+  uint64_t min_row=0;
+  unsigned int results_matching[pwebhooks_length];
+  struct TestLookupPendingWebhooks_Closure cls = {
+    .webhooks_to_cmp_length = pwebhooks_length,
+    .webhooks_to_cmp = pwebhooks,
+    .results_matching = results_matching,
+    .results_length = 0
+  };
+  memset (results_matching, 0, sizeof (unsigned int) * pwebhooks_length);
+  if (0 > plugin->lookup_all_webhooks (plugin->cls,
+                                       instance->instance.id,
+                                       min_row,
+                                       max_results,
+                                       &lookup_pending_webhooks_cb,
+                                       &cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup all webhooks failed\n");
+    return 1;
+  }
+  if (pwebhooks_length != cls.results_length)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Lookup all webhooks failed: incorrect number of results\n");
+    return 1;
+  }
+  for (unsigned int i = 0; pwebhooks_length > i; ++i)
+  {
+    if (1 != cls.results_matching[i])
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Lookup all webhooks failed: mismatched data\n");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+
+/**
+ * Tests deleting a pending webhook.
+ *
+ * @param instance the instance to delete the pending webhook from.
+ * @param pwebhook the pending webhook that should be deleted.
+ * @param expected_result the result that we expect the plugin to return.
+ * @return 0 when successful, 1 otherwise.
+ */
+static int
+test_delete_pending_webhook (const struct PendingWebhookData *webhook,
+                             enum GNUNET_DB_QueryStatus expected_result)
+{
+  TEST_COND_RET_ON_FAIL (expected_result ==
+                         plugin->delete_pending_webhook (plugin->cls,
+                                                         
webhook->webhook_serial),
+                         "Delete webhook failed\n");
+  return 0;
+}
+
+
+/**
+ * Closure for pending webhook tests.
+ */
+struct TestPendingWebhooks_Closure
+{
+  /**
+   * The instance to use for this test.
+   */
+  struct InstanceData instance;
+
+  /**
+   * The array of pending webhooks.
+   */
+  struct PendingWebhookData pwebhooks[2];
+};
+
+
+/**
+ * Sets up the data structures used in the pending webhook tests.
+ *
+ * @param cls the closure to fill with test data.
+ */
+static void
+pre_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
+{
+  /* Instance */
+  make_instance ("test_inst_pending_webhooks",
+                 &cls->instance);
+
+  /* Webhooks */
+  make_pending_webhook (1,
+                        &cls->pwebhooks[0]);
+
+  make_pending_webhook (4,
+                        &cls->pwebhooks[1]);
+  cls->pwebhooks[1].pwebhook.url = "https://test.com";;
+  cls->pwebhooks[1].pwebhook.http_method = "POST";
+  cls->pwebhooks[1].pwebhook.header = "Authorization:XYJAO5R06EO";
+  cls->pwebhooks[1].pwebhook.body = "$Amount";
+}
+
+
+/**
+ * Handles all teardown after testing.
+ *
+ * @param cls the closure containing memory to be freed.
+ */
+static void
+post_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
+{
+  free_instance_data (&cls->instance);
+}
+
+
+/**
+ * Runs the tests for pending webhooks.
+ *
+ * @param cls the container of the test data.
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+run_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
+{
+  /* Test that insert without an instance fails */
+  TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
+                                                 &cls->pwebhooks[0],
+                                                 
GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+
+  /* Insert the instance */
+  TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
+                                          
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+
+  /* Test inserting a pending webhook */
+  TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
+                                                 &cls->pwebhooks[0],
+                                                 
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  /* Test pending webhook update */
+  cls->pwebhooks[0].pwebhook.next_attempt = GNUNET_TIME_absolute_get ();
+
+  TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
+                                                 &cls->pwebhooks[0],
+                                                 
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
+                                                 &cls->pwebhooks[1],
+                                                 
GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+
+  /* Test collective pending webhook lookup */
+  TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
+                                                 &cls->pwebhooks[1],
+                                                 
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  TEST_RET_ON_FAIL (test_lookup_future_webhook (&cls->instance,
+                                                 1,
+                                                 cls->pwebhooks));
+  TEST_RET_ON_FAIL (test_lookup_pending_webhook (&cls->instance,
+                                                 2,
+                                                 cls->pwebhooks));
+  TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
+                                              2,
+                                              cls->pwebhooks));
+
+  /* Test webhook deletion */
+  TEST_RET_ON_FAIL (test_delete_pending_webhook (&cls->pwebhooks[1],
+                                                 
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  /* Test double deletion fails */
+  TEST_RET_ON_FAIL (test_delete_pending_webhook (&cls->pwebhooks[1],
+                                                 
GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
+  TEST_RET_ON_FAIL (test_delete_pending_webhook (&cls->pwebhooks[0],
+                                                 
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+  TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
+                                              0,
+                                              NULL));
+  return 0;
+}
+
+
+/**
+ * Takes care of pending webhook testing.
+ *
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+test_pending_webhooks (void)
+{
+  struct TestPendingWebhooks_Closure test_cls;
+  pre_test_pending_webhooks (&test_cls);
+  int test_result = run_test_pending_webhooks (&test_cls);
+  post_test_pending_webhooks (&test_cls);
+  return test_result;
+}
+
+
 /**
  * Function that runs all tests.
  *
@@ -7699,6 +8259,7 @@ run_tests (void)
   TEST_RET_ON_FAIL (test_kyc ());
   TEST_RET_ON_FAIL (test_templates ());
   TEST_RET_ON_FAIL (test_webhooks ());
+  TEST_RET_ON_FAIL (test_pending_webhooks ());
   return 0;
 }
 
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 0e70ec02..c2823d92 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-2021 Taler Systems SA
+  Copyright (C) 2014-2023 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
@@ -307,6 +307,7 @@ struct TALER_MERCHANTDB_ProductDetails
  *
  * @param cls a `json_t *` JSON array to build
  * @param template_id ID of the template
+ * @param template_description description of the template
  */
 typedef void
 (*TALER_MERCHANTDB_TemplatesCallback)(void *cls,
@@ -325,7 +326,7 @@ struct TALER_MERCHANTDB_TemplateDetails
   char *template_description;
 
   /**
-   * Base64-encoded product image, or NULL.
+   * Base64-encoded image, or NULL.
    */
   char *image;
 
@@ -341,10 +342,11 @@ struct TALER_MERCHANTDB_TemplateDetails
  *
  * @param cls a `json_t *` JSON array to build
  * @param webhook_id ID of the webhook
+ * @param event_type event of the webhook
  */
 typedef void
 (*TALER_MERCHANTDB_WebhooksCallback)(void *cls,
-                                      const char *webhook_id,
+                                     const char *webhook_id,
                                      const char *event_type);
 
 
@@ -353,6 +355,7 @@ typedef void
  */
 struct TALER_MERCHANTDB_WebhookDetails
 {
+
   /**
    * event of the webhook.
    */
@@ -368,13 +371,11 @@ struct TALER_MERCHANTDB_WebhookDetails
    */
   char *http_method;
 
-
   /**
    * Header template of the webhook.
    */
   char *header_template;
 
-
   /**
    * Body template of the webhook.
    */
@@ -382,6 +383,92 @@ struct TALER_MERCHANTDB_WebhookDetails
 
 };
 
+/**
+ * Typically called by `lookup_webhook_by_event`.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param webhook_serial reference to the configured webhook template.
+ * @param next_attempt is the time we should make the next request to the 
webhook.
+ * @param url to make request to
+ * @param http_method use for the webhook
+ * @param header of the webhook
+ * @param body of the webhook
+ */
+typedef void
+(*TALER_MERCHANTDB_WebhookDetailCallback)(void *cls,
+                                          uint64_t webhook_serial,
+                                          const char *event_type,
+                                          const char *url,
+                                          const char *http_method,
+                                          const char *header_template,
+                                          const char *body_template);
+
+
+
+/**
+ * Typically called by `lookup_pending_webhooks`.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param webhook_serial reference to the configured webhook template.
+ * @param next_attempt is the time we should make the next request to the 
webhook.
+ * @param retries how often have we tried this request to the webhook.
+ * @param url to make request to
+ * @param http_method use for the webhook
+ * @param header of the webhook
+ * @param body of the webhook
+ */
+typedef void
+(*TALER_MERCHANTDB_PendingWebhooksCallback)(void *cls,
+                                            uint64_t webhook_serial,
+                                            struct GNUNET_TIME_Absolute 
next_attempt,
+                                            uint32_t retries,
+                                            const char *url,
+                                            const char *http_method,
+                                            const char *header,
+                                            const char *body);
+
+
+/**
+ * Details about the pending webhook.
+ */
+struct TALER_MERCHANTDB_PendingWebhookDetails
+{
+  /**
+   * How often have we tried this request so far.
+   */
+  uint32_t retries;
+
+  /**
+   * Identifies when we should make the next request to the webhook. 0 for 
unknown,
+   * #GNUNET_TIME_UNIT_FOREVER_ABS for never.
+   */
+  struct GNUNET_TIME_Absolute next_attempt;
+
+  /**
+   * URL of the webhook. The customer will be redirected on this url.
+   */
+  char *url;
+
+  /**
+   * Http method used for the webhook.
+   */
+  char *http_method;
+
+
+  /**
+   * Header of the webhook.
+   */
+  char *header;
+
+
+  /**
+   * Body of the webhook.
+   */
+  char *body;
+
+};
+
+
 
 /**
  * Filter preferences.
@@ -2600,7 +2687,7 @@ struct TALER_MERCHANTDB_Plugin
  * Insert details about a particular webhook.
  *
  * @param cls closure
- * @param instance_id instance to insert template for
+ * @param instance_id instance to insert webhook for
  * @param webhook_id webhook identifier of webhook to insert
  * @param wb the webhook details to insert
  * @return database result code
@@ -2630,6 +2717,118 @@ struct TALER_MERCHANTDB_Plugin
                      const char *webhook_id,
                      const struct TALER_MERCHANTDB_WebhookDetails *wb);
 
+  /**
+   * Lookup webhook by event
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup webhook for
+   * @param event_type event that we need to put in the pending webhook
+   * @param[out] cb set to the webhook details on success
+   * @param cb_cls callback closure
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_webhook_by_event)(void *cls,
+                             const char *instance_id,
+                             const char *event_type,
+                             TALER_MERCHANTDB_WebhookDetailCallback cb,
+                             void *cb_cls);
+
+/**
+ * Insert webhook in the pending webhook.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert webhook for
+ * @param webhook_serial webhook to insert in the pending webhook
+ * @param url to make the request to
+ * @param http_method for the webhook
+ * @param header of the webhook
+ * @param body of the webhook
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*insert_pending_webhook)(void *cls,
+                            const char *instance_id,
+                            uint64_t webhook_serial,
+                            const char *url,
+                            const char *http_method,
+                            const char *header,
+                            const char *body);
+
+  /**
+   * Lookup the webhook that need to be send in priority. These webhooks are 
not successfully
+   * send.
+   *
+   * @param cls closure
+   * @param cb pending webhook callback
+   * @param cb_cls callback closure
+   */
+  // WHERE next_attempt <= now ORDER BY next_attempt ASC
+  enum GNUNET_DB_QueryStatus
+  (*lookup_pending_webhook)(void *cls,
+                            TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                            void *cb_cls);
+
+  /**
+   * Lookup future webhook in the pending webhook that need to be send.
+   * With that we can know how long the system can 'sleep'.
+   *
+   * @param cls closure
+   * @param cb pending webhook callback
+   * @param cb_cls callback closure
+   */
+  // ORDER BY next_attempt ASC LIMIT 1
+  enum GNUNET_DB_QueryStatus
+  (*lookup_future_webhook)(void *cls,
+                           TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                            void *cb_cls);
+
+  /**
+   * Lookup all the webhooks in the pending webhook.
+   * Use by the administrator
+   *
+   * @param cls closure
+   * @param instance_id to lookup webhooks for this instance particularly
+   * @param min_row to see the list of the pending webhook that it is started 
with this minimum row.
+   * @param max_results to see the list of the pending webhook that it is end 
with this max results.
+   * @param cb pending webhook callback
+   * @param cb_cls callback closure
+   */
+  // WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial 
ASC LIMIT max_results
+  enum GNUNET_DB_QueryStatus
+  (*lookup_all_webhooks)(void *cls,
+                         const char *instance_id,
+                         uint64_t min_row,
+                         uint32_t max_results,
+                         TALER_MERCHANTDB_PendingWebhooksCallback cb,
+                         void *cb_cls);
+
+
+/**
+ * Update the pending webhook. It is use if the webhook can't be send.
+ *
+ * @param cls closure
+ * @param webhook_serial webhook that need to be update
+ * @param next_attempt when we should make the next request to the webhook
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*update_pending_webhook)(void *cls,
+                            uint64_t webhook_serial,
+                            struct GNUNET_TIME_Absolute next_attempt);
+                            // maybe add: http status of failure?
+
+/**
+ * Delete a webhook in the pending webhook if it is successfull
+ *
+ * @param cls closure
+ * @param webhook_serial webhook that need to be delete in the pending webhook
+ * @return database result code
+ */
+  enum GNUNET_DB_QueryStatus
+  (*delete_pending_webhook)(void *cls,
+                            uint64_t webhook_serial);
+
 };
 
 #endif
diff --git a/src/testing/#testing_api_cmd_post_products.c# 
b/src/testing/#testing_api_cmd_post_products.c#
deleted file mode 100644
index be3c3071..00000000
--- a/src/testing/#testing_api_cmd_post_products.c#
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2020 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 testing_api_cmd_post_products.c
- * @brief command to test POST /products
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <taler/taler_exchange_service.h>
-#include <taler/taler_testing_lib.h>
-#include "taler_merchant_service.h"
-#include "taler_merchant_testing_lib.h"
-
-
-/**
- * State of a "POST /products" CMD.
- */
-struct PostProductsState
-{
-
-  /**
-   * Handle for a "GET product" request.
-   */
-  struct TALER_MERCHANT_ProductsPostHandle *iph;
-
-  /**
-   * The interpreter state.
-   */
-  struct TALER_TESTING_Interpreter *is;
-
-  /**
-   * Base URL of the merchant serving the request.
-   */
-  const char *merchant_url;
-
-  /**
-   * ID of the product to run POST for.
-   */
-  const char *product_id;
-
-  /**
-   * description of the product
-   */
-  const char *description;
-
-  /**
-   * Map from IETF BCP 47 language tags to localized descriptions
-   */
-  json_t *description_i18n;
-
-  /**
-   * unit in which the product is measured (liters, kilograms, packages, etc.)
-   */
-  const char *unit;
-
-  /**
-   * the price for one @a unit of the product
-   */
-  struct TALER_Amount price;
-
-  /**
-   * base64-encoded product image
-   */
-  char *image;
-
-  /**
-   * list of taxes paid by the merchant
-   */
-  json_t *taxes;
-
-  /**
-   * in @e units, -1 to indicate "infinite" (i.e. electronic books)
-   */
-  int64_t total_stock;
-
-  /**
-   * where the product is in stock
-   */
-  json_t *address;
-
-  /**
-   * when the next restocking is expected to happen, 0 for unknown,
-   */
-  struct GNUNET_TIME_Timestamp next_restock;
-
-  /**
-   * Expected HTTP response code.
-   */
-  unsigned int http_status;
-
-};
-
-
-/**
- * Callback for a POST /products operation.
- *
- * @param cls closure for this function
- * @param hr response being processed
- */
-static void
-post_products_cb (void *cls,
-                  const struct TALER_MERCHANT_HttpResponse *hr)
-{
-  struct PostProductsState *pis = cls;
-
-  pis->iph = NULL;
-  if (pis->http_status != hr->http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u (%d) to command %s\n",
-                hr->http_status,
-                (int) hr->ec,
-                TALER_TESTING_interpreter_get_current_label (pis->is));
-    TALER_TESTING_interpreter_fail (pis->is);
-    return;
-  }
-  switch (hr->http_status)
-  {
-  case MHD_HTTP_NO_CONTENT:
-    break;
-  case MHD_HTTP_UNAUTHORIZED:
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    break;
-  case MHD_HTTP_CONFLICT:
-    break;
-  default:
-    GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Unhandled HTTP status %u for POST /products.\n",
-                hr->http_status);
-  }
-  TALER_TESTING_interpreter_next (pis->is);
-}
-
-
-/**
- * Run the "POST /products" CMD.
- *
- *
- * @param cls closure.
- * @param cmd command being run now.
- * @param is interpreter state.
- */
-static void
-post_products_run (void *cls,
-                   const struct TALER_TESTING_Command *cmd,
-                   struct TALER_TESTING_Interpreter *is)
-{
-  struct PostProductsState *pis = cls;
-
-  pis->is = is;
-  pis->iph = TALER_MERCHANT_products_post (is->ctx,
-                                           pis->merchant_url,
-                                           pis->product_id,
-                                           pis->description,
-                                           pis->description_i18n,
-                                           pis->unit,
-                                           &pis->price,
-                                           pis->image,
-                                           pis->taxes,
-                                           pis->total_stock,
-                                           pis->address,
-                                           pis->next_restock,
-                                           &post_products_cb,
-                                           pis);
-  GNUNET_assert (NULL != pis->iph);
-}
-
-
-/**
- * Offers information from the POST /products CMD state to other
- * commands.
- *
- * @param cls closure
- * @param[out] ret result (could be anything)
- * @param trait name of the trait
- * @param index index number of the object to extract.
- * @return #GNUNET_OK on success
- */
-static int
-post_products_traits (void *cls,
-                      const void **ret,
-                      const char *trait,
-                      unsigned int index)
-{
-  struct PostProductsState *pps = cls;
-  struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_product_description (&pps->description),
-    TALER_TESTING_make_trait_i18n_description (pps->description_i18n),
-    TALER_TESTING_make_trait_product_unit (&pps->unit),
-    TALER_TESTING_make_trait_amount (&pps->price),
-    TALER_TESTING_make_trait_product_image (
-      (const char **) &pps->image),
-    TALER_TESTING_make_trait_taxes (pps->taxes),
-    TALER_TESTING_make_trait_product_stock (&pps->total_stock),
-    TALER_TESTING_make_trait_address (pps->address),
-    TALER_TESTING_make_trait_timestamp (0,
-                                        &pps->next_restock),
-    TALER_TESTING_make_trait_product_id (&pps->product_id),
-    TALER_TESTING_trait_end (),
-  };
-
-  return TALER_TESTING_get_trait (traits,
-                                  ret,
-                                  trait,
-                                  index);
-}
-
-
-/**
- * Free the state of a "POST product" CMD, and possibly
- * cancel a pending operation thereof.
- *
- * @param cls closure.
- * @param cmd command being run.
- */
-static void
-post_products_cleanup (void *cls,
-                       const struct TALER_TESTING_Command *cmd)
-{
-  struct PostProductsState *pis = cls;
-
-  if (NULL != pis->iph)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "POST /products operation did not complete\n");
-    TALER_MERCHANT_products_post_cancel (pis->iph);
-  }
-  json_decref (pis->description_i18n);
-  GNUNET_free (pis->image);
-  json_decref (pis->taxes);
-  json_decref (pis->address);
-  GNUNET_free (pis);
-}
-
-
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_post_products2 (
-  const char *label,
-  const char *merchant_url,
-  const char *product_id,
-  const char *description,
-  json_t *description_i18n,
-  const char *unit,
-  const char *price,
-  const char *image,
-  json_t *taxes,
-  int64_t total_stock,
-  json_t *address,
-  struct GNUNET_TIME_Timestamp next_restock,
-  unsigned int http_status)
-{
-  struct PostProductsState *pis;
-
-  GNUNET_assert ((NULL == taxes) ||
-                 json_is_array (taxes));
-  GNUNET_assert ((NULL == description_i18n) ||
-                 json_is_object (description_i18n));
-  pis = GNUNET_new (struct PostProductsState);
-  pis->merchant_url = merchant_url;
-  pis->product_id = product_id;
-  pis->http_status = http_status;
-  pis->description = description;
-  pis->description_i18n = description_i18n; /* ownership taken */
-  pis->unit = unit;
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_string_to_amount (price,
-                                         &pis->price));
-  pis->image = GNUNET_strdup (image);
-  pis->taxes = taxes; /* ownership taken */
-  pis->total_stock = total_stock;
-  pis->address = address; /* ownership taken */
-  pis->next_restock = next_restock;
-  {
-    struct TALER_TESTING_Command cmd = {
-      .cls = pis,
-      .label = label,
-      .run = &post_products_run,
-      .cleanup = &post_products_cleanup,
-      .traits = &post_products_traits
-    };
-
-    return cmd;
-  }
-}
-
-
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_post_products (const char *label,
-                                          const char *merchant_url,
-                                          const char *product_id,
-                                          const char *description,
-                                          const char *price,
-                                          unsigned int http_status)
-{
-  return TALER_TESTING_cmd_merchant_post_products2 (
-    label,
-    merchant_url,
-    product_id,
-    description,
-    json_pack ("{s:s}", "en", description),
-    "test-unit",
-    price,
-    "",
-    json_array (),
-    4,
-    json_pack ("{s:s}", "street", "my street"),
-    GNUNET_TIME_UNIT_ZERO_TS,
-    http_status);
-}
-
-
-/* end of testing_api_cmd_post_products.c */

-- 
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]