gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: work towards taler-merchant-kycc


From: gnunet
Subject: [taler-merchant] branch master updated: work towards taler-merchant-kyccheck helper
Date: Sat, 07 Sep 2024 23:21:28 +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 23812272 work towards taler-merchant-kyccheck helper
23812272 is described below

commit 23812272e6445aed81c4caefb59053ff0b597431
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Sep 7 23:21:17 2024 +0200

    work towards taler-merchant-kyccheck helper
---
 src/backend/taler-merchant-depositcheck.c          |   4 +-
 src/backend/taler-merchant-httpd.c                 |   3 +
 .../taler-merchant-httpd_private-get-accounts.c    |   3 +
 ...r-merchant-httpd_private-get-instances-ID-kyc.c | 140 +++---
 ...chant-httpd_private-patch-token-families-SLUG.c |   7 +-
 .../taler-merchant-httpd_private-post-account.c    |   2 +-
 .../taler-merchant-httpd_private-post-orders.c     | 178 +++----
 src/backend/taler-merchant-httpd_statics.c         |   4 +
 src/backend/taler-merchant-kyccheck.c              | 510 +++++++++++++++++++--
 src/backenddb/Makefile.am                          |   2 +
 src/backenddb/merchant-0010.sql                    |   2 +-
 src/backenddb/pg_account_kyc_get_status.c          |   5 +-
 src/backenddb/pg_account_kyc_set_status.c          |  41 +-
 src/backenddb/pg_account_kyc_set_status.h          |   8 +-
 src/backenddb/pg_account_kyc_set_status.sql        |  84 ++--
 src/backenddb/pg_get_kyc_status.c                  | 109 +++++
 src/backenddb/pg_get_kyc_status.h                  |  62 +++
 src/backenddb/pg_insert_account.c                  |   3 +-
 src/backenddb/pg_insert_account.h                  |   2 -
 src/backenddb/pg_select_account.c                  |   1 +
 src/backenddb/pg_select_account_by_uri.c           |   1 +
 src/backenddb/pg_select_accounts.c                 |  35 +-
 src/backenddb/plugin_merchantdb_postgres.c         |   3 +
 src/backenddb/test_merchantdb.c                    |  21 +-
 src/include/taler_merchantdb_plugin.h              |  52 ++-
 25 files changed, 962 insertions(+), 320 deletions(-)

diff --git a/src/backend/taler-merchant-depositcheck.c 
b/src/backend/taler-merchant-depositcheck.c
index ff90c505..3b725596 100644
--- a/src/backend/taler-merchant-depositcheck.c
+++ b/src/backend/taler-merchant-depositcheck.c
@@ -463,17 +463,17 @@ deposit_get_cb (
                   dr->details.accepted.kyc_ok,
                   TALER_B2S (&w->coin_pub));
       now = GNUNET_TIME_timestamp_get ();
+      /* FIXME: probably should NOT clobber limits, etc, and
+         ONLY set kyc_ok (always to false?) */
       qs = db_plugin->account_kyc_set_status (
         db_plugin->cls,
         w->instance_id,
         &w->h_wire,
         exchange_url,
-        dr->details.accepted.requirement_row,
         now,
         MHD_HTTP_ACCEPTED,
         TALER_EC_NONE,
         NULL,
-        0,
         NULL,
         false,
         dr->details.accepted.kyc_ok);
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index a121251e..e46a2db5 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -2011,15 +2011,18 @@ url_handler (void *cls,
  * Callback invoked with information about a bank account.
  *
  * @param cls closure with a `struct TMH_MerchantInstance *`
+ * @param merchant_priv private key of the merchant instance
  * @param acc details about the account
  */
 static void
 add_account_cb (void *cls,
+                const struct TALER_MerchantPrivateKeyP *merchant_priv,
                 const struct TALER_MERCHANTDB_AccountDetails *acc)
 {
   struct TMH_MerchantInstance *mi = cls;
   struct TMH_WireMethod *wm;
 
+  (void) merchant_priv;
   wm = GNUNET_new (struct TMH_WireMethod);
   wm->h_wire = acc->h_wire;
   wm->payto_uri = GNUNET_strdup (acc->payto_uri);
diff --git a/src/backend/taler-merchant-httpd_private-get-accounts.c 
b/src/backend/taler-merchant-httpd_private-get-accounts.c
index e420a0e8..a5791960 100644
--- a/src/backend/taler-merchant-httpd_private-get-accounts.c
+++ b/src/backend/taler-merchant-httpd_private-get-accounts.c
@@ -26,14 +26,17 @@
  * Add account details to our JSON array.
  *
  * @param cls a `json_t *` JSON array to build
+ * @param merchant_priv private key of the merchant instance
  * @param ad details about the account
  */
 static void
 add_account (void *cls,
+             const struct TALER_MerchantPrivateKeyP *merchant_priv,
              const struct TALER_MERCHANTDB_AccountDetails *ad)
 {
   json_t *pa = cls;
 
+  (void) merchant_priv;
   GNUNET_assert (0 ==
                  json_array_append_new (
                    pa,
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c 
b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
index f8bad978..69e962d0 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
@@ -101,11 +101,6 @@ struct ExchangeKycRequest
    */
   struct TALER_EXCHANGE_KycCheckHandle *kyc;
 
-  /**
-   * KYC number used by the exchange.
-   */
-  uint64_t exchange_kyc_serial;
-
   /**
    * Our account's payto URI.
    */
@@ -448,6 +443,34 @@ handle_kyc_timeout (void *cls)
 }
 
 
+/**
+ * Pack the given @a limit into the JSON @a limits array.
+ *
+ * @param limit account limit to pack
+ * @param[in,out] limits JSON array to extend
+ */
+static void
+pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
+            json_t *limits)
+{
+  json_t *jl;
+
+  jl = GNUNET_JSON_PACK (
+    TALER_JSON_pack_kycte ("operation_type",
+                           limit->operation_type),
+    GNUNET_JSON_pack_time_rel ("timeframe",
+                               limit->timeframe),
+    TALER_JSON_pack_amount ("threshold",
+                            &limit->threshold),
+    GNUNET_JSON_pack_bool ("soft_limit",
+                           limit->soft_limit)
+    );
+  GNUNET_assert (0 ==
+                 json_array_append_new (limits,
+                                        jl));
+}
+
+
 /**
  * We are done with the KYC request @a ekr.  Remove it from the work list and
  * check if we are done overall.
@@ -512,91 +535,43 @@ store_kyc_status (
   const struct TALER_EXCHANGE_KycStatus *ks,
   const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
 {
-  unsigned int dl_cnt = 0;
+  json_t *jlimits;
+  enum GNUNET_DB_QueryStatus qs;
 
+  jlimits = json_array ();
+  GNUNET_assert (NULL != jlimits);
   for (unsigned int i = 0; i<account_kyc_status->limits_length; i++)
   {
     const struct TALER_EXCHANGE_AccountLimit *limit
       = &account_kyc_status->limits[i];
 
-    if (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT ==
-        limit->operation_type)
-      dl_cnt++;
+    pack_limit (limit,
+                jlimits);
   }
 
+  qs = TMH_db->account_kyc_set_status (
+    TMH_db->cls,
+    ekr->kc->mi->settings.id,
+    &ekr->h_wire,
+    ekr->exchange_url,
+    GNUNET_TIME_timestamp_get (),
+    ks->hr.http_status,
+    ks->hr.ec,
+    &account_kyc_status->access_token,
+    jlimits,
+    account_kyc_status->aml_review,
+    false);
+  json_decref (jlimits);
+  if (qs < 0)
   {
-    struct TALER_MERCHANTDB_DepositLimits dls[GNUNET_NZL (dl_cnt)];
-    enum GNUNET_DB_QueryStatus qs;
-    unsigned int off = 0;
-
-    for (unsigned int i = 0; i<account_kyc_status->limits_length; i++)
-    {
-      const struct TALER_EXCHANGE_AccountLimit *limit
-        = &account_kyc_status->limits[i];
-      struct TALER_MERCHANTDB_DepositLimits *dl
-        = &dls[off];
-
-      if (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT !=
-          limit->operation_type)
-        continue;
-      dl->timeframe = limit->timeframe;
-      dl->threshold = limit->threshold;
-      dl->soft_limit = limit->soft_limit;
-      off++;
-    }
-    qs = TMH_db->account_kyc_set_status (
-      TMH_db->cls,
-      ekr->kc->mi->settings.id,
-      &ekr->h_wire,
-      ekr->exchange_url,
-      ekr->exchange_kyc_serial,
-      GNUNET_TIME_timestamp_get (),
-      ks->hr.http_status,
-      ks->hr.ec,
-      &account_kyc_status->access_token,
-      dl_cnt,
-      dls,
-      account_kyc_status->aml_review,
-      false);
-    if (qs < 0)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Failed to store KYC status in database!\n");
-      return false;
-    }
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to store KYC status in database!\n");
+    return false;
   }
   return true;
 }
 
 
-/**
- * Pack the given @a limit into the JSON @a limits array.
- *
- * @param limit account limit to pack
- * @param[in,out] limits JSON array to extend
- */
-static void
-pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
-            json_t *limits)
-{
-  json_t *jl;
-
-  jl = GNUNET_JSON_PACK (
-    TALER_JSON_pack_kycte ("operation_type",
-                           limit->operation_type),
-    GNUNET_JSON_pack_time_rel ("timeframe",
-                               limit->timeframe),
-    TALER_JSON_pack_amount ("threshold",
-                            &limit->threshold),
-    GNUNET_JSON_pack_bool ("soft_limit",
-                           limit->soft_limit)
-    );
-  GNUNET_assert (0 ==
-                 json_array_append_new (limits,
-                                        jl));
-}
-
-
 /**
  * Return JSON array with AccountLimit objects giving
  * the current limits for this exchange.
@@ -753,12 +728,10 @@ return_auth_required (
     kc->mi->settings.id,
     &ekr->h_wire,
     ekr->exchange_url,
-    ekr->exchange_kyc_serial,
     GNUNET_TIME_timestamp_get (),
     ks->hr.http_status,
     ks->hr.ec,
     NULL,
-    0,
     NULL,
     false,
     true);
@@ -838,12 +811,10 @@ exchange_check_cb (
         kc->mi->settings.id,
         &ekr->h_wire,
         ekr->exchange_url,
-        ekr->exchange_kyc_serial,
         GNUNET_TIME_timestamp_get (),
         MHD_HTTP_NO_CONTENT,
         TALER_EC_NONE,
         NULL,
-        0,
         NULL,
         false,
         true);
@@ -885,12 +856,10 @@ exchange_check_cb (
         kc->mi->settings.id,
         &ekr->h_wire,
         ekr->exchange_url,
-        ekr->exchange_kyc_serial,
         GNUNET_TIME_timestamp_get (),
         ks->hr.http_status,
         ks->hr.ec,
         NULL,
-        0,
         NULL,
         false,
         true);
@@ -951,6 +920,7 @@ determine_eligible_accounts (
   /* For all accounts of the exchange */
   for (unsigned int i = 0; i<keys->accounts_len; i++)
   {
+    /* FIXME: move into convenience function in libtalerexchange? See also 
taler-merchant-kyccheck.c! */
     struct TALER_EXCHANGE_WireAccount *account
       = &keys->accounts[i];
     bool account_restricted = false;
@@ -1138,18 +1108,12 @@ kyc_status_cb (
          <,
          STALE_KYC_TIMEOUT)) )
     return; /* KYC ok, ignore! */
-  if (0 == exchange_kyc_serial)
-  {
-    kc->kyc_serial_pending = true;
-    return;
-  }
   kc->response_code = MHD_HTTP_OK;
   ekr = GNUNET_new (struct ExchangeKycRequest);
   GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
                                kc->exchange_pending_tail,
                                ekr);
   ekr->h_wire = *h_wire;
-  ekr->exchange_kyc_serial = exchange_kyc_serial;
   ekr->exchange_url = GNUNET_strdup (exchange_url);
   ekr->payto_uri = GNUNET_strdup (payto_uri);
   ekr->last_check = last_check;
diff --git 
a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c 
b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
index 755ed4c9..56235514 100644
--- a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
+++ b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
@@ -67,6 +67,7 @@ TMH_private_patch_token_family_SLUG (const struct 
TMH_RequestHandler *rh,
                                     &details.duration),
     GNUNET_JSON_spec_end ()
   };
+  struct GNUNET_TIME_Relative validity;
 
   GNUNET_assert (NULL != mi);
   GNUNET_assert (NULL != slug);
@@ -82,12 +83,12 @@ TMH_private_patch_token_family_SLUG (const struct 
TMH_RequestHandler *rh,
              : MHD_NO;
   }
 
-  struct GNUNET_TIME_Relative validity = GNUNET_TIME_absolute_get_difference (
+  validity = GNUNET_TIME_absolute_get_difference (
     details.valid_after.abs_time,
     details.valid_before.abs_time);
 
-  // Check if start_time is before valid_before
-  if (0 == validity.rel_value_us)
+  /* Check if start_time is before valid_before */
+  if (GNUNET_TIME_relative_is_zero (validity))
   {
     GNUNET_break_op (0);
     GNUNET_JSON_parse_free (spec);
diff --git a/src/backend/taler-merchant-httpd_private-post-account.c 
b/src/backend/taler-merchant-httpd_private-post-account.c
index dbaac7ba..44d9f084 100644
--- a/src/backend/taler-merchant-httpd_private-post-account.c
+++ b/src/backend/taler-merchant-httpd_private-post-account.c
@@ -123,6 +123,7 @@ TMH_private_post_account (const struct TMH_RequestHandler 
*rh,
     struct TALER_MERCHANTDB_AccountDetails ad = {
       .payto_uri = wm->payto_uri,
       .salt = wm->wire_salt,
+      .instance_id = mi->settings.id,
       .h_wire = wm->h_wire,
       .credit_facade_url = wm->credit_facade_url,
       .credit_facade_credentials = wm->credit_facade_credentials,
@@ -131,7 +132,6 @@ TMH_private_post_account (const struct TMH_RequestHandler 
*rh,
     enum GNUNET_DB_QueryStatus qs;
 
     qs = TMH_db->insert_account (TMH_db->cls,
-                                 mi->settings.id,
                                  &ad);
     switch (qs)
     {
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 4761fbdc..8fcbf7c4 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -1544,23 +1544,26 @@ set_token_family (struct OrderContext *oc,
     return GNUNET_SYSERR;
   }
 
-  struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
-
-  /* Verify that the token family is valid right now. */
-  if (GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_after, >, now)
-      ||
-      GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_before, <=, 
now)
-      )
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Token family expired or not yet valid\n");
-    reply_with_error (oc,
-                      /* TODO: HTTP Status Code GONE would be more elegant,
-                               but that is already used to indicate that a 
product is out of stock. */
-                      MHD_HTTP_CONFLICT,
-                      
TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_NOT_VALID,
-                      key_details.token_family.slug);
-    return GNUNET_SYSERR;
+    struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
+
+    /* Verify that the token family is valid right now. */
+    if (GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_after, >, 
now)
+        ||
+        GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_before, <=,
+                                   now)
+        )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Token family expired or not yet valid\n");
+      reply_with_error (oc,
+                        /* TODO: HTTP Status Code GONE would be more elegant,
+                                 but that is already used to indicate that a 
product is out of stock. */
+                        MHD_HTTP_CONFLICT,
+                        
TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_NOT_VALID,
+                        key_details.token_family.slug);
+      return GNUNET_SYSERR;
+    }
   }
 
   /* slug is not needed */
@@ -1607,8 +1610,6 @@ set_token_family (struct OrderContext *oc,
       /* There is no matching key for this token family yet. */
       /* We have to generate a fresh key pair. */
       /* If public key is NULL, private key must also be NULL */
-      GNUNET_assert (NULL == key_details.priv.private_key);
-
       enum GNUNET_DB_QueryStatus iqs;
       struct GNUNET_CRYPTO_BlindSignPrivateKey *priv;
       struct GNUNET_CRYPTO_BlindSignPublicKey *pub;
@@ -1617,6 +1618,7 @@ set_token_family (struct OrderContext *oc,
           GNUNET_TIME_absolute_add (min_valid_after.abs_time,
                                     key_details.token_family.duration));
 
+      GNUNET_assert (NULL == key_details.priv.private_key);
       if (GNUNET_TIME_timestamp_cmp (min_valid_after,
                                      <,
                                      key_details.token_family.valid_after))
@@ -1640,55 +1642,55 @@ set_token_family (struct OrderContext *oc,
                                             /* TODO: Make cipher and key 
length configurable */
                                             GNUNET_CRYPTO_BSA_RSA,
                                             4096);
-
-      struct TALER_TokenIssuePublicKeyP token_pub = {
-        .public_key = pub,
-      };
-      struct TALER_TokenIssuePrivateKeyP token_priv = {
-        .private_key = priv,
-      };
-
-      iqs = TMH_db->insert_token_family_key (TMH_db->cls,
-                                             slug,
-                                             &token_pub,
-                                             &token_priv,
-                                             min_valid_after,
-                                             valid_before);
-
-      GNUNET_CRYPTO_blind_sign_priv_decref (priv);
-
-      if (iqs <= 0)
       {
-        enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
-        unsigned int http_status = 0;
+        struct TALER_TokenIssuePublicKeyP token_pub = {
+          .public_key = pub,
+        };
+        struct TALER_TokenIssuePrivateKeyP token_priv = {
+          .private_key = priv,
+        };
 
-        switch (iqs)
+        iqs = TMH_db->insert_token_family_key (TMH_db->cls,
+                                               slug,
+                                               &token_pub,
+                                               &token_priv,
+                                               min_valid_after,
+                                               valid_before);
+        GNUNET_CRYPTO_blind_sign_priv_decref (priv);
+
+        if (iqs <= 0)
         {
-        case GNUNET_DB_STATUS_HARD_ERROR:
-          http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-          ec = TALER_EC_GENERIC_DB_STORE_FAILED;
-          break;
-        case GNUNET_DB_STATUS_SOFT_ERROR:
-        case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-          http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-          ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
-          break;
-        case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-          /* case listed to make compilers happy */
-          GNUNET_assert (0);
+          enum TALER_ErrorCode ec = 
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+          unsigned int http_status = 0;
+
+          switch (iqs)
+          {
+          case GNUNET_DB_STATUS_HARD_ERROR:
+            http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+            ec = TALER_EC_GENERIC_DB_STORE_FAILED;
+            break;
+          case GNUNET_DB_STATUS_SOFT_ERROR:
+          case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+            http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+            ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
+            break;
+          case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+            /* case listed to make compilers happy */
+            GNUNET_assert (0);
+          }
+
+          GNUNET_break (0);
+          reply_with_error (oc,
+                            http_status,
+                            ec,
+                            "token_family_slug");
+          return GNUNET_SYSERR;
         }
 
-        GNUNET_break (0);
-        reply_with_error (oc,
-                          http_status,
-                          ec,
-                          "token_family_slug");
-        return GNUNET_SYSERR;
+        key.pub = token_pub;
+        key.valid_after = min_valid_after;
+        key.valid_before = valid_before;
       }
-
-      key.pub = token_pub;
-      key.valid_after = min_valid_after;
-      key.valid_before = valid_before;
     }
     else
     {
@@ -1771,8 +1773,9 @@ serialize_order (struct OrderContext *oc)
   for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
   {
     json_t *keys = json_array ();
-    struct TALER_MerchantContractTokenFamily *family = &oc->parse_choices.
-                                                       token_families[i];
+    struct TALER_MerchantContractTokenFamily *family
+      = &oc->parse_choices.token_families[i];
+    json_t *jfamily;
 
     for (unsigned int j = 0; j<family->keys_len; j++)
     {
@@ -1797,11 +1800,13 @@ serialize_order (struct OrderContext *oc)
                                     key.valid_before)
         );
 
-      GNUNET_assert (0 == json_array_append_new (keys, jkey));
+      GNUNET_assert (0 ==
+                     json_array_append_new (keys,
+                                            jkey));
     }
 
     /* TODO: Add 'details' field. */
-    json_t *jfamily = GNUNET_JSON_PACK (
+    jfamily = GNUNET_JSON_PACK (
       GNUNET_JSON_pack_string ("name",
                                family->name),
       GNUNET_JSON_pack_string ("description",
@@ -1822,19 +1827,22 @@ serialize_order (struct OrderContext *oc)
 
   for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
   {
-    struct TALER_MerchantContractChoice *choice = 
&oc->parse_choices.choices[i];
-
+    const struct TALER_MerchantContractChoice *choice
+      = &oc->parse_choices.choices[i];
     json_t *inputs = json_array ();
     json_t *outputs = json_array ();
 
+    GNUNET_assert (NULL != inputs);
+    GNUNET_assert (NULL != outputs);
     for (unsigned int j = 0; j<choice->inputs_len; j++)
     {
-      struct TALER_MerchantContractInput *input = &choice->inputs[j];
+      const struct TALER_MerchantContractInput *input
+        = &choice->inputs[j];
+      json_t *jinput;
 
       /* For now, only tokens are supported */
       GNUNET_assert (TALER_MCIT_TOKEN == input->type);
-
-      json_t *jinput = GNUNET_JSON_PACK (
+      jinput = GNUNET_JSON_PACK (
         GNUNET_JSON_pack_string ("kind",
                                  TMH_string_from_contract_input_type (input->
                                                                       type)),
@@ -1846,17 +1854,20 @@ serialize_order (struct OrderContext *oc)
                                     input->details.token.valid_after)
         );
 
-      GNUNET_assert (0 == json_array_append_new (inputs, jinput));
+      GNUNET_assert (0 ==
+                     json_array_append_new (inputs,
+                                            jinput));
     }
 
     for (unsigned int j = 0; j<choice->outputs_len; j++)
     {
       struct TALER_MerchantContractOutput *output = &choice->outputs[j];
+      json_t *joutput;
 
       /* For now, only tokens are supported */
       GNUNET_assert (TALER_MCOT_TOKEN == output->type);
 
-      json_t *joutput = GNUNET_JSON_PACK (
+      joutput = GNUNET_JSON_PACK (
         GNUNET_JSON_pack_string ("kind",
                                  TMH_string_from_contract_output_type (output->
                                                                        type)),
@@ -1868,17 +1879,24 @@ serialize_order (struct OrderContext *oc)
                                     output->details.token.valid_after)
         );
 
-      GNUNET_assert (0 == json_array_append (outputs, joutput));
+      GNUNET_assert (0 ==
+                     json_array_append_new (outputs,
+                                            joutput));
     }
 
-    json_t *jchoice = GNUNET_JSON_PACK (
-      GNUNET_JSON_pack_array_incref ("inputs",
-                                     inputs),
-      GNUNET_JSON_pack_array_incref ("outputs",
-                                     outputs)
-      );
+    {
+      json_t *jchoice
+        = GNUNET_JSON_PACK (
+            GNUNET_JSON_pack_array_incref ("inputs",
+                                           inputs),
+            GNUNET_JSON_pack_array_incref ("outputs",
+                                           outputs)
+            );
 
-    GNUNET_assert (0 == json_array_append (choices, jchoice));
+      GNUNET_assert (0 ==
+                     json_array_append_new (choices,
+                                            jchoice));
+    }
   }
 
   oc->serialize_order.contract = GNUNET_JSON_PACK (
diff --git a/src/backend/taler-merchant-httpd_statics.c 
b/src/backend/taler-merchant-httpd_statics.c
index 72f81d85..a82dafba 100644
--- a/src/backend/taler-merchant-httpd_statics.c
+++ b/src/backend/taler-merchant-httpd_statics.c
@@ -316,6 +316,10 @@ TMH_statics_init ()
  * Nicely shut down.
  */
 void __attribute__ ((destructor))
+get_statics_fini (void);
+
+/* Declaration avoids compiler warning */
+void __attribute__ ((destructor))
 get_statics_fini ()
 {
   for (unsigned int i = 0; i<loaded_length; i++)
diff --git a/src/backend/taler-merchant-kyccheck.c 
b/src/backend/taler-merchant-kyccheck.c
index 297106de..dfb2be53 100644
--- a/src/backend/taler-merchant-kyccheck.c
+++ b/src/backend/taler-merchant-kyccheck.c
@@ -22,7 +22,10 @@
 #include <gnunet/gnunet_util_lib.h>
 #include <jansson.h>
 #include <pthread.h>
+#include <regex.h>
 #include <taler/taler_dbevents.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_exchange_service.h>
 #include "taler_merchant_bank_lib.h"
 #include "taler_merchantdb_lib.h"
 #include "taler_merchantdb_plugin.h"
@@ -89,11 +92,32 @@ struct Account
    */
   struct Inquiry *i_tail;
 
+  /**
+   * Merchant instance this account belongs to.
+   */
+  char *instance_id;
+
   /**
    * The payto-URI of this account.
    */
   char *merchant_account_uri;
 
+  /**
+   * Wire hash of the merchant bank account (with the
+   * respective salt).
+   */
+  struct TALER_MerchantWireHashP h_wire;
+
+  /**
+   * Private key of the instance.
+   */
+  union TALER_AccountPrivateKeyP ap;
+
+  /**
+   * Hash of the @e merchant_account_uri.
+   */
+  struct TALER_PaytoHashP h_payto;
+
   /**
    * Database generation when this account
    * was last active.
@@ -132,6 +156,54 @@ struct Inquiry
    */
   struct Account *a;
 
+  /**
+   * AccountLimits that apply to the account, NULL
+   * if unknown.
+   */
+  json_t *jlimits;
+
+  /**
+   * Handle for the actual HTTP request to the exchange.
+   */
+  struct TALER_EXCHANGE_KycCheckHandle *kyc;
+
+  /**
+   * Access token for the /kyc-info API.
+   */
+  struct TALER_AccountAccessTokenP access_token;
+
+  /**
+   * Last time we called the /kyc-check endpoint.
+   */
+  struct GNUNET_TIME_Timestamp last_kyc_check;
+
+  /**
+   * When is the next KYC check due?
+   */
+  struct GNUNET_TIME_Absolute due;
+
+  /**
+   * When should the current KYC time out?
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * Current exponential backoff.
+   */
+  struct GNUNET_TIME_Relative backoff;
+
+  /**
+   * Last HTTP status returned by the exchange from
+   * the /kyc-check endpoint.
+   */
+  unsigned int last_http_status;
+
+  /**
+   * Last Taler error code returned by the exchange from
+   * the /kyc-check endpoint.
+   */
+  enum TALER_ErrorCode last_ec;
+
   /**
    * Did we not run this inquiry due to limits?
    */
@@ -143,10 +215,16 @@ struct Inquiry
   bool kyc_ok;
 
   /**
-   * True if we did this account's KYC AUTH transfer.
+   * True if merchant did perform this account's KYC AUTH transfer and @e 
access_token is set.
    */
   bool auth_ok;
 
+  /**
+   * True if the account is known to be currently under
+   * investigation by AML staff.
+   */
+  bool aml_review;
+
 };
 
 
@@ -238,12 +316,13 @@ static bool at_limit;
 
 
 /**
- * Do KYC check work for the give inquiry in @a i.
+ * Check about performing a /kyc-check request with the
+ * exchange for the given inquiry.
  *
- * @param i inquiry to work on
+ * @param cls a `struct Inquiry` to process
  */
 static void
-inquiry_work (struct Inquiry *i);
+inquiry_work (void *cls);
 
 
 /**
@@ -292,10 +371,216 @@ end_inquiry (void)
 }
 
 
+/**
+ * Pack the given @a limit into the JSON @a limits array.
+ *
+ * @param limit account limit to pack
+ * @param[in,out] limits JSON array to extend
+ */
+static void
+pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
+            json_t *limits)
+{
+  json_t *jl;
+
+  jl = GNUNET_JSON_PACK (
+    TALER_JSON_pack_kycte ("operation_type",
+                           limit->operation_type),
+    GNUNET_JSON_pack_time_rel ("timeframe",
+                               limit->timeframe),
+    TALER_JSON_pack_amount ("threshold",
+                            &limit->threshold),
+    GNUNET_JSON_pack_bool ("soft_limit",
+                           limit->soft_limit)
+    );
+  GNUNET_assert (0 ==
+                 json_array_append_new (limits,
+                                        jl));
+}
+
+
+/**
+ * Store KYC response from the exchange in the
+ * local database.
+ *
+ * @param[in,out] i inquiry context, jlimits is updated
+ * @param ks HTTP response details
+ * @param account_kyc_status account KYC status details
+ */
 static void
-inquiry_work (struct Inquiry *i)
+store_kyc_status (
+  struct Inquiry *i,
+  const struct TALER_EXCHANGE_KycStatus *ks,
+  const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
 {
-  //  enum GNUNET_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
+  json_t *jlimits;
+
+  jlimits = json_array ();
+  GNUNET_assert (NULL != jlimits);
+  for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
+  {
+    const struct TALER_EXCHANGE_AccountLimit *limit
+      = &account_kyc_status->limits[j];
+
+    pack_limit (limit,
+                jlimits);
+  }
+  json_decref (i->jlimits);
+  i->jlimits = jlimits;
+  // FIXME: update more of i?
+
+  qs = db_plugin->account_kyc_set_status (
+    db_plugin->cls,
+    i->a->instance_id,
+    &i->a->h_wire,
+    i->e->keys->exchange_url,
+    GNUNET_TIME_timestamp_get (),
+    ks->hr.http_status,
+    ks->hr.ec,
+    &account_kyc_status->access_token,
+    jlimits,
+    account_kyc_status->aml_review,
+    MHD_HTTP_OK == ks->hr.http_status);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+}
+
+
+/**
+ * Function called with the result of a KYC check.
+ *
+ * @param cls a `struct Inquiry *`
+ * @param ks the account's KYC status details
+ */
+static void
+exchange_check_cb (
+  void *cls,
+  const struct TALER_EXCHANGE_KycStatus *ks)
+{
+  struct Inquiry *i = cls;
+
+  i->kyc = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Checking KYC status of `%s' at `%s' is %u\n",
+              i->a->merchant_account_uri,
+              i->e->keys->exchange_url,
+              ks->hr.http_status);
+  switch (ks->hr.http_status)
+  {
+  case MHD_HTTP_OK:
+    store_kyc_status (i,
+                      ks,
+                      &ks->details.ok);
+    break;
+  case MHD_HTTP_ACCEPTED:
+    store_kyc_status (i,
+                      ks,
+                      &ks->details.accepted);
+    break;
+  case MHD_HTTP_NO_CONTENT:
+    {
+      enum GNUNET_DB_QueryStatus qs;
+
+      // FIXME: update i?
+      qs = db_plugin->account_kyc_set_status (
+        db_plugin->cls,
+        i->a->instance_id,
+        &i->a->h_wire,
+        i->e->keys->exchange_url,
+        GNUNET_TIME_timestamp_get (),
+        MHD_HTTP_NO_CONTENT,
+        TALER_EC_NONE,
+        NULL,
+        NULL,
+        false,
+        true);
+      if (qs < 0)
+      {
+        GNUNET_break (0);
+        global_ret = EXIT_FAILURE;
+        GNUNET_SCHEDULER_shutdown ();
+        return;
+      }
+    }
+    break;
+  case MHD_HTTP_FORBIDDEN: /* bad signature */
+  case MHD_HTTP_NOT_FOUND: /* account unknown */
+  case MHD_HTTP_CONFLICT: /* no account_pub known */
+    {
+      enum GNUNET_DB_QueryStatus qs;
+
+      // FIXME: update i?
+      qs = db_plugin->account_kyc_set_status (
+        db_plugin->cls,
+        i->a->instance_id,
+        &i->a->h_wire,
+        i->e->keys->exchange_url,
+        GNUNET_TIME_timestamp_get (),
+        ks->hr.http_status,
+        ks->hr.ec,
+        NULL,
+        NULL,
+        false,
+        true);
+      if (qs < 0)
+      {
+        GNUNET_break (0);
+        global_ret = EXIT_FAILURE;
+        GNUNET_SCHEDULER_shutdown ();
+        return;
+      }
+    }
+    break;
+  default:
+    {
+      enum GNUNET_DB_QueryStatus qs;
+
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Exchange responded with HTTP status %u (%d) to /kyc-check 
request!\n",
+                  ks->hr.http_status,
+                  ks->hr.ec);
+      // FIXME: update i?
+      qs = db_plugin->account_kyc_set_status (
+        db_plugin->cls,
+        i->a->instance_id,
+        &i->a->h_wire,
+        i->e->keys->exchange_url,
+        GNUNET_TIME_timestamp_get (),
+        ks->hr.http_status,
+        ks->hr.ec,
+        NULL /* access token */,
+        NULL /* jlimits */,
+        false /* in_aml_review? well, unknown... */,
+        true /* kyc_ok? well, unknown... */);
+      if (qs < 0)
+      {
+        GNUNET_break (0);
+        global_ret = EXIT_FAILURE;
+        GNUNET_SCHEDULER_shutdown ();
+        return;
+      }
+      break;
+    }
+  }
+  end_inquiry ();
+}
+
+
+static void
+inquiry_work (void *cls)
+{
+  struct Inquiry *i = cls;
+
+  i->task = NULL;
+  // FIXME: update i->due, i->timeout, i->backoff
+  if (! GNUNET_TIME_absolute_is_past (i->due))
+    goto finish;
 
   GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
   if (OPEN_INQUIRY_LIMIT <= active_inquiries)
@@ -308,21 +593,29 @@ inquiry_work (struct Inquiry *i)
   }
   at_limit = false;
 
-#if 0
-  active_inquiries++;
-  // FIXME: do actual work!
-  qs = db_plugin->select_open_transfers (db_plugin->cls,
-                                         limit,
-                                         &start_inquiry,
-                                         NULL);
-  if (qs < 0)
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Checking KYC status of `%s' at `%s'\n",
+              i->a->merchant_account_uri,
+              i->e->keys->exchange_url);
+  i->kyc = TALER_EXCHANGE_kyc_check (
+    ctx,
+    i->e->keys->exchange_url,
+    &i->a->h_payto,
+    &i->a->ap,
+    GNUNET_TIME_absolute_get_remaining (i->timeout),
+    &exchange_check_cb,
+    i);
+  if (NULL == i->kyc)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to obtain open transfers from database\n");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
+    GNUNET_break (0);
+    i->task
+      = GNUNET_SCHEDULER_add_at (i->timeout,
+                                 &inquiry_work,
+                                 i);
+    goto finish;
   }
-#endif
+  active_inquiries++;
+finish:
   if ( (0 == active_inquiries) &&
        (test_mode) )
   {
@@ -334,6 +627,80 @@ inquiry_work (struct Inquiry *i)
 }
 
 
+/**
+ * Check if the account @a could work with exchange that
+ * has keys @a keys.
+ *
+ * @param keys the keys of an exchange
+ * @param a an account
+ */
+static bool
+is_eligible (struct TALER_EXCHANGE_Keys *keys,
+             struct Account *a)
+{
+  /* For all accounts of the exchange */
+  for (unsigned int i = 0; i<keys->accounts_len; i++)
+  {
+    /* FIXME: move into convenience function in libtalerexchange? See also 
taler-merchant-httpd_private-get-instances-ID-kyc.c*/
+    struct TALER_EXCHANGE_WireAccount *account
+      = &keys->accounts[i];
+    bool account_restricted = false;
+
+    /* KYC auth transfers are never supported with conversion */
+    if (NULL != account->conversion_url)
+      continue;
+
+    /* filter by source account by credit_restrictions */
+    for (unsigned int j = 0; j<account->credit_restrictions_length; j++)
+    {
+      const struct TALER_EXCHANGE_AccountRestriction *ar
+        = &account->credit_restrictions[j];
+
+      switch (ar->type)
+      {
+      case TALER_EXCHANGE_AR_INVALID:
+        continue;
+      case TALER_EXCHANGE_AR_DENY:
+        account_restricted = true;
+        break;
+      case TALER_EXCHANGE_AR_REGEX:
+        {
+          regex_t ex;
+          bool allowed = false;
+
+          if (0 != regcomp (&ex,
+                            ar->details.regex.posix_egrep,
+                            REG_NOSUB | REG_EXTENDED))
+          {
+            GNUNET_break_op (0);
+            continue;
+          }
+          if (regexec (&ex,
+                       a->merchant_account_uri,
+                       0, NULL,
+                       REG_STARTEND))
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                        "Merchant account `%s' allowed by regex\n",
+                        a->merchant_account_uri);
+            allowed = true;
+          }
+          regfree (&ex);
+          if (! allowed)
+            account_restricted = true;
+          break;
+        }
+      }   /* end switch */
+    }   /* end loop over credit restrictions */
+    if (account_restricted)
+      continue;
+    /* exchange account is allowed, add it */
+    return true;
+  }
+  return false;
+}
+
+
 /**
  * Start the KYC checking for account @a at exchange @a e.
  *
@@ -345,6 +712,7 @@ start_inquiry (struct Exchange *e,
                struct Account *a)
 {
   struct Inquiry *i;
+  enum GNUNET_DB_QueryStatus qs;
 
   i = GNUNET_new (struct Inquiry);
   i->e = e;
@@ -352,7 +720,25 @@ start_inquiry (struct Exchange *e,
   GNUNET_CONTAINER_DLL_insert (a->i_head,
                                a->i_tail,
                                i);
-  // FIXME: initial import from DB here!?
+  qs = db_plugin->get_kyc_status (db_plugin->cls,
+                                  a->merchant_account_uri,
+                                  a->instance_id,
+                                  e->keys->exchange_url,
+                                  &i->auth_ok,
+                                  &i->access_token,
+                                  &i->kyc_ok,
+                                  &i->last_http_status,
+                                  &i->last_ec,
+                                  &i->last_kyc_check,
+                                  &i->aml_review,
+                                  &i->jlimits);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
   inquiry_work (i);
 }
 
@@ -375,11 +761,45 @@ stop_inquiry (struct Inquiry *i)
     GNUNET_SCHEDULER_cancel (i->task);
     i->task = NULL;
   }
-  // FIXME: other clean-ups related to i here!
+  if (NULL != i->kyc)
+  {
+    TALER_EXCHANGE_kyc_check_cancel (i->kyc);
+    i->kyc = NULL;
+  }
+  if (NULL != i->jlimits)
+  {
+    json_decref (i->jlimits);
+    i->jlimits = NULL;
+  }
   GNUNET_free (i);
 }
 
 
+/**
+ * Stop KYC inquiry for account @a at exchange @a e.
+ *
+ * @param e an exchange
+ * @param a an account
+ */
+static void
+stop_inquiry_at (struct Exchange *e,
+                 struct Account *a)
+{
+  for (struct Inquiry *i = a->i_head;
+       NULL != i;
+       i = i->next)
+  {
+    if (e == i->e)
+    {
+      stop_inquiry (i);
+      return;
+    }
+  }
+  /* strange, there should have been a match! */
+  GNUNET_break (0);
+}
+
+
 /**
  * Start inquries for all exchanges on account @a a.
  *
@@ -391,8 +811,10 @@ start_inquiries (struct Account *a)
   for (struct Exchange *e = e_head;
        NULL != e;
        e = e->next)
-    start_inquiry (e,
-                   a);
+    if (is_eligible (e->keys,
+                     a))
+      start_inquiry (e,
+                     a);
 }
 
 
@@ -415,17 +837,21 @@ stop_inquiries (struct Account *a)
  * Callback invoked with information about a bank account.
  *
  * @param cls closure
+ * @param merchant_priv private key of the merchant instance
  * @param ad details about the account
  */
 static void
 account_cb (
   void *cls,
+  const struct TALER_MerchantPrivateKeyP *merchant_priv,
   const struct TALER_MERCHANTDB_AccountDetails *ad)
 {
   const char *payto_uri = ad->payto_uri;
 
   if (! ad->active)
     return;
+  if (NULL == merchant_priv)
+    return; /* instance was deleted */
   for (struct Account *a = a_head;
        NULL != a;
        a = a->next)
@@ -442,7 +868,17 @@ account_cb (
     struct Account *a = GNUNET_new (struct Account);
 
     a->account_gen = database_gen;
-    a->merchant_account_uri = GNUNET_strdup (payto_uri);
+    a->merchant_account_uri
+      = GNUNET_strdup (ad->payto_uri);
+    a->instance_id
+      = GNUNET_strdup (ad->instance_id);
+    a->h_wire
+      = ad->h_wire;
+    a->ap.merchant_priv
+      = *merchant_priv;
+    TALER_payto_hash (a->merchant_account_uri,
+                      &a->h_payto);
+
     GNUNET_CONTAINER_DLL_insert (a_head,
                                  a_tail,
                                  a);
@@ -537,8 +973,28 @@ find_keys (const char *exchange_url)
     if (0 == strcmp (e->keys->exchange_url,
                      keys->exchange_url))
     {
-      TALER_EXCHANGE_keys_decref (e->keys);
+      struct TALER_EXCHANGE_Keys *old_keys = e->keys;
+
       e->keys = keys;
+      for (struct Account *a = a_head;
+           NULL != a;
+           a = a->next)
+      {
+        bool was_eligible = is_eligible (old_keys,
+                                         a);
+        bool now_eligible = is_eligible (keys,
+                                         a);
+
+        if (was_eligible == now_eligible)
+          continue; /* no change, do nothing */
+        if (was_eligible)
+          stop_inquiry_at (e,
+                           a);
+        else /* is_eligible */
+          start_inquiry (e,
+                         a);
+      }
+      TALER_EXCHANGE_keys_decref (old_keys);
       return;
     }
   }
@@ -551,7 +1007,9 @@ find_keys (const char *exchange_url)
        NULL != a;
        a = a->next)
   {
-    if (a->account_gen < database_gen)
+    if ( (a->account_gen == database_gen) &&
+         (is_eligible (e->keys,
+                       a)) )
       start_inquiry (e,
                      a);
   }
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 1703f20c..792bfb53 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -27,6 +27,7 @@ sql_DATA = \
   merchant-0008.sql \
   merchant-0009.sql \
   merchant-0010.sql \
+  merchant-0011.sql \
   drop.sql
 
 BUILT_SOURCES = \
@@ -102,6 +103,7 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
   pg_lookup_pending_deposits.h pg_lookup_pending_deposits.c \
   pg_insert_instance.h pg_insert_instance.c \
   pg_account_kyc_set_status.h pg_account_kyc_set_status.c \
+  pg_get_kyc_status.h pg_get_kyc_status.c \
   pg_account_kyc_get_status.h pg_account_kyc_get_status.c \
   pg_delete_instance_private_key.h pg_delete_instance_private_key.c \
   pg_purge_instance.h pg_purge_instance.c \
diff --git a/src/backenddb/merchant-0010.sql b/src/backenddb/merchant-0010.sql
index e2839aa4..44a2a113 100644
--- a/src/backenddb/merchant-0010.sql
+++ b/src/backenddb/merchant-0010.sql
@@ -15,7 +15,7 @@
 --
 
 -- @file merchant-0010.sql
--- @brief Remove dead aml_decision column
+-- @brief Remove dead aml_decision column and add new ones
 -- @author Christian Grothoff
 
 -- Everything in one big transaction
diff --git a/src/backenddb/pg_account_kyc_get_status.c 
b/src/backenddb/pg_account_kyc_get_status.c
index c2e97276..1eee0416 100644
--- a/src/backenddb/pg_account_kyc_get_status.c
+++ b/src/backenddb/pg_account_kyc_get_status.c
@@ -80,7 +80,7 @@ kyc_status_cb (void *cls,
   for (unsigned int i = 0; i < num_results; i++)
   {
     struct TALER_MerchantWireHashP h_wire;
-    uint64_t kyc_serial;
+    uint64_t kyc_serial = 0; /* deprecated */
     char *exchange_url;
     char *payto_uri;
     struct GNUNET_TIME_Timestamp last_check;
@@ -88,8 +88,6 @@ kyc_status_cb (void *cls,
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
                                             &h_wire),
-      GNUNET_PQ_result_spec_uint64 ("exchange_kyc_serial",
-                                    &kyc_serial),
       GNUNET_PQ_result_spec_string ("payto_uri",
                                     &payto_uri),
       GNUNET_PQ_result_spec_string ("exchange_url",
@@ -164,7 +162,6 @@ TMH_PG_account_kyc_get_status (
            "lookup_kyc_status",
            "SELECT"
            " h_wire"
-           ",exchange_kyc_serial"
            ",payto_uri"
            ",exchange_url"
            ",kyc_timestamp"
diff --git a/src/backenddb/pg_account_kyc_set_status.c 
b/src/backenddb/pg_account_kyc_set_status.c
index 2e2b7911..c4e8bd97 100644
--- a/src/backenddb/pg_account_kyc_set_status.c
+++ b/src/backenddb/pg_account_kyc_set_status.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2022 Taler Systems SA
+   Copyright (C) 2022-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
@@ -17,6 +17,7 @@
  * @file backenddb/pg_account_kyc_set_status.c
  * @brief Implementation of the account_kyc_set_status function for Postgres
  * @author Iván Ávalos
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <taler/taler_error_codes.h>
@@ -32,43 +33,30 @@ TMH_PG_account_kyc_set_status (
   const char *merchant_id,
   const struct TALER_MerchantWireHashP *h_wire,
   const char *exchange_url,
-  uint64_t exchange_kyc_serial,
   struct GNUNET_TIME_Timestamp timestamp,
   unsigned int exchange_http_status,
   enum TALER_ErrorCode exchange_ec_code,
   const struct TALER_AccountAccessTokenP *access_token,
-  unsigned int num_limits,
-  const struct TALER_MERCHANTDB_DepositLimits *limits,
+  const json_t *jlimits,
   bool in_aml_review,
   bool kyc_ok)
 {
   struct PostgresClosure *pg = cls;
   uint32_t http_status32 = (uint32_t) exchange_http_status;
   uint32_t ec_code32 = (uint32_t) exchange_ec_code;
-  struct TALER_Amount thresholds[GNUNET_NZL (num_limits)];
-  struct GNUNET_TIME_Relative timeframes[GNUNET_NZL (num_limits)];
-  bool soft_limits[GNUNET_NZL (num_limits)];
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (merchant_id),
     GNUNET_PQ_query_param_auto_from_type (h_wire),
     GNUNET_PQ_query_param_string (exchange_url),
     GNUNET_PQ_query_param_timestamp (&timestamp),
-    GNUNET_PQ_query_param_uint64 (&exchange_kyc_serial),
     GNUNET_PQ_query_param_uint32 (&http_status32),
     GNUNET_PQ_query_param_uint32 (&ec_code32),
     NULL != access_token
     ? GNUNET_PQ_query_param_auto_from_type (access_token)
     : GNUNET_PQ_query_param_null (),
-    TALER_PQ_query_param_array_amount_with_currency (
-      num_limits,
-      thresholds,
-      pg->conn),
-    GNUNET_PQ_query_param_array_rel_time (num_limits,
-                                          timeframes,
-                                          pg->conn),
-    GNUNET_PQ_query_param_array_bool (num_limits,
-                                      soft_limits,
-                                      pg->conn),
+    NULL != jlimits
+    ? TALER_PQ_query_param_json (jlimits)
+    : GNUNET_PQ_query_param_null (),
     GNUNET_PQ_query_param_bool (in_aml_review),
     GNUNET_PQ_query_param_bool (kyc_ok),
     GNUNET_PQ_query_param_end
@@ -91,24 +79,19 @@ TMH_PG_account_kyc_set_status (
            "  out_no_instance AS no_instance"
            " ,out_no_account AS no_account"
            " FROM merchant_do_account_kyc_set_status"
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);");
-  for (unsigned int i=0; i<num_limits; i++)
-  {
-    const struct TALER_MERCHANTDB_DepositLimits *limit
-      = &limits[i];
-
-    thresholds[i] = limit->threshold;
-    timeframes[i] = limit->timeframe;
-    soft_limits[i] = limit->soft_limit;
-  }
+           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
   qs = GNUNET_PQ_eval_prepared_singleton_select (
     pg->conn,
     "account_kyc_set_status",
     params,
     rs);
-  GNUNET_PQ_cleanup_query_params_closures (params);
   if (qs <= 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
     return qs;
+  }
   GNUNET_break (! no_instance);
   GNUNET_break (! no_account);
   return qs;
diff --git a/src/backenddb/pg_account_kyc_set_status.h 
b/src/backenddb/pg_account_kyc_set_status.h
index 3c5891b3..d970c62e 100644
--- a/src/backenddb/pg_account_kyc_set_status.h
+++ b/src/backenddb/pg_account_kyc_set_status.h
@@ -32,13 +32,11 @@
  * @param merchant_id merchant backend instance ID
  * @param h_wire hash of the wire account to check
  * @param exchange_url base URL of the exchange to check
- * @param exchange_kyc_serial serial number for our account at the exchange (0 
if unknown)
  * @param timestamp timestamp to store
  * @param exchange_http_status HTTP status code returned last by the exchange
  * @param exchange_ec_code Taler error code returned last by the exchange
  * @param access_token access token for the KYC process, NULL for none
- * @param num_limits length of the @a limits array
- * @param limits array with deposit limits returned by the exchange
+ * @param jlimits JSON array with AccountLimits returned by the exchange
  * @param in_aml_review true if the exchange says the account is under review
  * @param kyc_ok current KYC status (true for satisfied)
  * @return database result code
@@ -49,13 +47,11 @@ TMH_PG_account_kyc_set_status (
   const char *merchant_id,
   const struct TALER_MerchantWireHashP *h_wire,
   const char *exchange_url,
-  uint64_t exchange_kyc_serial,
   struct GNUNET_TIME_Timestamp timestamp,
   unsigned int exchange_http_status,
   enum TALER_ErrorCode exchange_ec_code,
   const struct TALER_AccountAccessTokenP *access_token,
-  unsigned int num_limits,
-  const struct TALER_MERCHANTDB_DepositLimits *limits,
+  const json_t *jlimits,
   bool in_aml_review,
   bool kyc_ok);
 
diff --git a/src/backenddb/pg_account_kyc_set_status.sql 
b/src/backenddb/pg_account_kyc_set_status.sql
index f72ab6ec..ce86a764 100644
--- a/src/backenddb/pg_account_kyc_set_status.sql
+++ b/src/backenddb/pg_account_kyc_set_status.sql
@@ -15,18 +15,17 @@
 --
 
 
-CREATE OR REPLACE FUNCTION merchant_do_account_kyc_set_status (
+DROP FUNCTION IF EXISTS merchant_do_account_kyc_set_status;
+
+CREATE FUNCTION merchant_do_account_kyc_set_status (
   IN in_merchant_id TEXT,
   IN in_h_wire BYTEA,
   IN in_exchange_url TEXT,
   IN in_timestamp INT8,
-  IN in_exchange_kyc_serial INT8,
   IN in_exchange_http_status INT4,
   IN in_exchange_ec_code INT4,
-  IN in_access_token BYTEA,
-  IN ina_thresholds taler_amount_currency[],
-  IN ina_timeframes INT8[],
-  IN ina_soft_limits BOOL[],
+  IN in_access_token BYTEA, -- can be NULL
+  IN in_jlimits TEXT,
   IN in_aml_active BOOL,
   IN in_kyc_ok BOOL,
   OUT out_no_instance BOOL,
@@ -36,8 +35,6 @@ AS $$
 DECLARE
   my_merchant_id INT8;
   my_account_serial INT8;
-  ini_cat INT8;
-  rec RECORD;
 BEGIN
 
 out_no_instance=FALSE;
@@ -67,49 +64,40 @@ THEN
   RETURN;
 END IF;
 
-INSERT INTO merchant_kyc
-  (kyc_timestamp
-  ,kyc_ok
-  ,exchange_kyc_serial
-  ,account_serial
-  ,exchange_url
-  ,deposit_thresholds
-  ,deposit_timeframes
-  ,deposit_limits_are_soft
-  ,aml_review
-  ,exchange_http_status
-  ,exchange_ec_code
-  ,access_token)
-VALUES
-  (in_timestamp
-  ,in_kyc_ok
-  ,in_exchange_kyc_serial
-  ,my_account_serial
-  ,in_exchange_url
-  ,ina_thresholds
-  ,ina_timeframes
-  ,ina_soft_limits
-  ,in_aml_active
-  ,in_exchange_http_status
-  ,in_exchange_ec_code
-  ,in_access_token)
-  ON CONFLICT DO NOTHING;
+UPDATE merchant_kyc
+   SET kyc_timestamp=in_timestamp
+      ,kyc_ok=in_kyc_ok
+      ,jaccount_limits=in_jlimits
+      ,aml_review=in_aml_active
+      ,exchange_http_status=in_exchange_http_status
+      ,exchange_ec_code=in_exchange_ec_code
+      ,access_token=in_access_token
+ WHERE account_serial=my_account_serial
+   AND exchange_url=in_exchange_url;
 
 IF NOT FOUND
 THEN
-  UPDATE merchant_kyc
-     SET exchange_kyc_serial=in_exchange_kyc_serial
-        ,kyc_timestamp=in_timestamp
-        ,kyc_ok=in_kyc_ok
-        ,deposit_thresholds=ina_thresholds
-        ,deposit_timeframes=ina_timeframes
-        ,deposit_limits_are_soft=ina_soft_limits
-        ,aml_review=in_aml_active
-        ,exchange_http_status=in_exchange_http_status
-        ,exchange_ec_code=in_exchange_ec_code
-        ,access_token=in_access_token
-   WHERE account_serial=my_account_serial
-     AND exchange_url=in_exchange_url;
+
+  INSERT INTO merchant_kyc
+    (kyc_timestamp
+    ,kyc_ok
+    ,account_serial
+    ,exchange_url
+    ,jaccount_limits
+    ,aml_review
+    ,exchange_http_status
+    ,exchange_ec_code
+    ,access_token)
+  VALUES
+    (in_timestamp
+    ,in_kyc_ok
+    ,my_account_serial
+    ,in_exchange_url
+    ,in_jlimits
+    ,in_aml_active
+    ,in_exchange_http_status
+    ,in_exchange_ec_code
+    ,in_access_token);
 END IF;
 
 -- Success!
diff --git a/src/backenddb/pg_get_kyc_status.c 
b/src/backenddb/pg_get_kyc_status.c
new file mode 100644
index 00000000..5c980bfd
--- /dev/null
+++ b/src/backenddb/pg_get_kyc_status.c
@@ -0,0 +1,109 @@
+/*
+   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_get_kyc_status.c
+ * @brief Implementation of the get_kyc_status 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_get_kyc_status.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_get_kyc_status (
+  void *cls,
+  const char *merchant_account_uri,
+  const char *instance_id,
+  const char *exchange_url,
+  bool *auth_ok,
+  struct TALER_AccountAccessTokenP *access_token,
+  bool *kyc_ok,
+  unsigned int *last_http_status,
+  enum TALER_ErrorCode *last_ec,
+  struct GNUNET_TIME_Timestamp *last_kyc_check,
+  bool *aml_review,
+  json_t **jlimits)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (merchant_account_uri),
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_end
+  };
+  uint32_t h32 = 0;
+  uint32_t e32 = 0;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_allow_null (
+      GNUNET_PQ_result_spec_auto_from_type ("access_token",
+                                            &access_token),
+      auth_ok),
+    GNUNET_PQ_result_spec_uint32 ("exchange_http_status",
+                                  &h32),
+    GNUNET_PQ_result_spec_uint32 ("exchange_ec_code",
+                                  &e32),
+    GNUNET_PQ_result_spec_bool ("kyc_ok",
+                                kyc_ok),
+    GNUNET_PQ_result_spec_timestamp ("kyc_timestamp",
+                                     last_kyc_check),
+    GNUNET_PQ_result_spec_bool ("aml_review",
+                                aml_review),
+    GNUNET_PQ_result_spec_allow_null (
+      TALER_PQ_result_spec_json ("jaccount_limits",
+                                 jlimits),
+      NULL),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "get_kyc_status",
+           "SELECT"
+           " access_token"
+           ",exchange_http_status"
+           ",exchange_ec_code"
+           ",kyc_ok"
+           ",kyc_timestamp"
+           ",aml_review"
+           ",jaccount_limits"
+           " FROM merchant_kyc mk"
+           " JOIN merchant_accounts"
+           "   USING (merchant_serial)"
+           " JOIN merchant_kyc"
+           "   USING (account_serial)"
+           " WHERE exchange_url=$3"
+           "   AND account_serial="
+           "   (SELECT account_serial"
+           "      FROM merchant_accounts"
+           "     WHERE payto_uri=$1"
+           "       AND merchant_serial="
+           "     (SELECT merchant_serial"
+           "        FROM merchant_instances"
+           "       WHERE merchant_id=$2));");
+  *jlimits = NULL;
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "get_kyc_status",
+                                                 params,
+                                                 rs);
+  *last_ec = (enum TALER_ErrorCode) (int) e32;
+  *last_http_status = (unsigned int) h32;
+  return qs;
+}
diff --git a/src/backenddb/pg_get_kyc_status.h 
b/src/backenddb/pg_get_kyc_status.h
new file mode 100644
index 00000000..729f37d5
--- /dev/null
+++ b/src/backenddb/pg_get_kyc_status.h
@@ -0,0 +1,62 @@
+/*
+   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_get_kyc_status.h
+ * @brief implementation of the get_kyc_status function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_GET_KYC_STATUS_H
+#define PG_GET_KYC_STATUS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Check an account's KYC status at an exchange.
+ *
+ * @param cls closure
+ * @param merchant_payto_uri  merchant backend instance ID
+ * @param instance_id the instance for which to check
+ * @param exchange_url base URL of the exchange
+ * @param[out] auth_ok true if @a access_token was set
+ * @param[out] access_token set to access token for /kyc-info
+ * @param[out] kyc_ok true if no urgent KYC work must be done for this account
+ * @param[out] last_http_status set to last HTTP status from exchange on 
/kyc-check
+ * @param[out] last_ec set to last Taler error code from exchange on /kyc-check
+ * @param[out] last_kyc_check set to time of last KYC check
+ * @param[out] aml_review set to true if the account is under AML review (if 
this exposed)
+ * @param[out] jlimits set to JSON array with AccountLimits, NULL if unknown 
(and likely defaults apply or KYC auth is urgently needed, see @a auth_ok)
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_get_kyc_status (
+  void *cls,
+  const char *merchant_account_uri,
+  const char *instance_id,
+  const char *exchange_url,
+  bool *auth_ok,
+  struct TALER_AccountAccessTokenP *access_token,
+  bool *kyc_ok,
+  unsigned int *last_http_status,
+  enum TALER_ErrorCode *last_ec,
+  struct GNUNET_TIME_Timestamp *last_kyc_check,
+  bool *aml_review,
+  json_t **jlimits);
+
+
+#endif
diff --git a/src/backenddb/pg_insert_account.c 
b/src/backenddb/pg_insert_account.c
index 04b0637c..504c4da5 100644
--- a/src/backenddb/pg_insert_account.c
+++ b/src/backenddb/pg_insert_account.c
@@ -29,12 +29,11 @@
 enum GNUNET_DB_QueryStatus
 TMH_PG_insert_account (
   void *cls,
-  const char *id,
   const struct TALER_MERCHANTDB_AccountDetails *account_details)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (id),
+    GNUNET_PQ_query_param_string (account_details->instance_id),
     GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
     GNUNET_PQ_query_param_auto_from_type (&account_details->salt),
     GNUNET_PQ_query_param_string (account_details->payto_uri),
diff --git a/src/backenddb/pg_insert_account.h 
b/src/backenddb/pg_insert_account.h
index 463bc527..758ce59e 100644
--- a/src/backenddb/pg_insert_account.h
+++ b/src/backenddb/pg_insert_account.h
@@ -29,14 +29,12 @@
  * Insert information about an instance's account into our database.
  *
  * @param cls closure
- * @param id identifier of the instance
  * @param account_details details about the account
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
 TMH_PG_insert_account (
   void *cls,
-  const char *id,
   const struct TALER_MERCHANTDB_AccountDetails *account_details);
 
 
diff --git a/src/backenddb/pg_select_account.c 
b/src/backenddb/pg_select_account.c
index 9d48e421..eab30377 100644
--- a/src/backenddb/pg_select_account.c
+++ b/src/backenddb/pg_select_account.c
@@ -57,6 +57,7 @@ TMH_PG_select_account (void *cls,
   };
 
   ad->h_wire = *h_wire;
+  ad->instance_id = id;
   check_connection (pg);
   PREPARE (pg,
            "select_account",
diff --git a/src/backenddb/pg_select_account_by_uri.c 
b/src/backenddb/pg_select_account_by_uri.c
index fafae088..70471eb4 100644
--- a/src/backenddb/pg_select_account_by_uri.c
+++ b/src/backenddb/pg_select_account_by_uri.c
@@ -59,6 +59,7 @@ TMH_PG_select_account_by_uri (void *cls,
   ad->credit_facade_url = NULL;
   ad->credit_facade_credentials = NULL;
   ad->payto_uri = GNUNET_strdup (payto_uri);
+  ad->instance_id = id;
   check_connection (pg);
   PREPARE (pg,
            "select_account_by_uri",
diff --git a/src/backenddb/pg_select_accounts.c 
b/src/backenddb/pg_select_accounts.c
index eaf26015..672db926 100644
--- a/src/backenddb/pg_select_accounts.c
+++ b/src/backenddb/pg_select_accounts.c
@@ -72,9 +72,12 @@ select_account_cb (void *cls,
   for (unsigned int i = 0; i < num_results; i++)
   {
     char *payto;
+    char *instance_id;
     char *facade_url = NULL;
     json_t *credential = NULL;
     struct TALER_MERCHANTDB_AccountDetails acc;
+    struct TALER_MerchantPrivateKeyP merchant_priv;
+    bool no_priv;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
                                             &acc.h_wire),
@@ -82,6 +85,8 @@ select_account_cb (void *cls,
                                             &acc.salt),
       GNUNET_PQ_result_spec_string ("payto_uri",
                                     &payto),
+      GNUNET_PQ_result_spec_string ("merchant_id",
+                                    &instance_id),
       GNUNET_PQ_result_spec_allow_null (
         GNUNET_PQ_result_spec_string ("credit_facade_url",
                                       &facade_url),
@@ -92,6 +97,10 @@ select_account_cb (void *cls,
         NULL),
       GNUNET_PQ_result_spec_bool ("active",
                                   &acc.active),
+      GNUNET_PQ_result_spec_allow_null (
+        GNUNET_PQ_result_spec_auto_from_type ("merchant_priv",
+                                              &merchant_priv),
+        &no_priv),
       GNUNET_PQ_result_spec_end
     };
 
@@ -104,10 +113,12 @@ select_account_cb (void *cls,
       lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
+    acc.instance_id = instance_id;
     acc.payto_uri = payto;
     acc.credit_facade_url = facade_url;
     acc.credit_facade_credentials = credential;
     lic->cb (lic->cb_cls,
+             no_priv ? NULL : &merchant_priv,
              &acc);
     GNUNET_PQ_cleanup_result (rs);
   }
@@ -138,16 +149,22 @@ TMH_PG_select_accounts (void *cls,
   PREPARE (pg,
            "select_accounts",
            "SELECT"
-           " h_wire"
-           ",salt"
-           ",payto_uri"
-           ",credit_facade_url"
-           ",credit_facade_credentials"
-           ",active"
-           " FROM merchant_accounts"
+           " ma.h_wire"
+           ",ma.salt"
+           ",ma.payto_uri"
+           ",ma.credit_facade_url"
+           ",ma.credit_facade_credentials"
+           ",ma.active"
+           ",mk.merchant_priv"
+           ",mi.merchant_id"
+           " FROM merchant_accounts ma"
+           " JOIN merchant_instances mi"
+           "   ON (mi.merchant_serial=ma.merchant_serial)"
+           " LEFT JOIN merchant_keys mk"
+           "   ON (mk.merchant_serial=ma.merchant_serial)"
            " WHERE"
-           "  ($1 IS NULL) OR"
-           "  (merchant_serial="
+           "  ($1::TEXT IS NULL) OR"
+           "  (ma.merchant_serial="
            "    (SELECT merchant_serial "
            "       FROM merchant_instances"
            "      WHERE merchant_id=$1));");
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 2bb74d32..690f066e 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -32,6 +32,7 @@
 #include "taler_merchantdb_plugin.h"
 #include "pg_helper.h"
 #include "pg_insert_otp.h"
+#include "pg_get_kyc_status.h"
 #include "pg_delete_otp.h"
 #include "pg_update_otp.h"
 #include "pg_select_otp.h"
@@ -581,6 +582,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_lookup_categories;
   plugin->select_category_by_name
     = &TMH_PG_select_category_by_name;
+  plugin->get_kyc_status
+    = &TMH_PG_get_kyc_status;
   plugin->select_category
     = &TMH_PG_select_category;
   plugin->update_category
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index c2028261..392511e1 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -98,7 +98,7 @@ struct InstanceData
  * @param instance the instance data to be filled.
  */
 static void
-make_instance (char *instance_id,
+make_instance (const char *instance_id,
                struct InstanceData *instance)
 {
   memset (instance,
@@ -451,7 +451,6 @@ test_insert_account (const struct InstanceData *instance,
 {
   TEST_COND_RET_ON_FAIL (expected_result ==
                          plugin->insert_account (plugin->cls,
-                                                 instance->instance.id,
                                                  account),
                          "Insert account failed\n");
   return 0;
@@ -513,7 +512,11 @@ pre_test_instances (struct TestInstances_Closure *cls)
 
   /* Accounts */
   make_account (&cls->accounts[0]);
+  cls->accounts[0].instance_id
+    = cls->instances[0].instance.id;
   make_account (&cls->accounts[1]);
+  cls->accounts[1].instance_id
+    = cls->instances[1].instance.id;
 }
 
 
@@ -3207,7 +3210,7 @@ pre_test_deposits (struct TestDeposits_Closure *cls)
 
   /* Account */
   make_account (&cls->account);
-
+  cls->account.instance_id = cls->instance.instance.id;
   /* Signing key */
   make_exchange_signkey (&cls->signkey);
 
@@ -4325,7 +4328,7 @@ pre_test_transfers (struct TestTransfers_Closure *cls)
 
   /* Account */
   make_account (&cls->account);
-
+  cls->account.instance_id = cls->instance.instance.id;
   /* Order */
   make_order ("test_transfers_od_1",
               &cls->order);
@@ -5039,7 +5042,7 @@ pre_test_refunds (struct TestRefunds_Closure *cls)
 
   /* Account */
   make_account (&cls->account);
-
+  cls->account.instance_id = cls->instance.instance.id;
   /* Signing key */
   make_exchange_signkey (&cls->signkey);
 
@@ -5357,6 +5360,7 @@ pre_test_lookup_orders_all_filters (
   make_instance ("test_inst_lookup_orders_all_filters",
                  &cls->instance);
   make_account (&cls->account);
+  cls->account.instance_id = cls->instance.instance.id;
   make_exchange_signkey (&cls->signkey);
   for (unsigned int i = 0; i < 64; ++i)
   {
@@ -5613,6 +5617,7 @@ test_kyc (void)
   make_instance ("test_kyc",
                  &instance);
   make_account (&account);
+  account.instance_id = instance.instance.id;
   TEST_RET_ON_FAIL (test_insert_instance (&instance,
                                           
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   TEST_RET_ON_FAIL (test_insert_account (&instance,
@@ -5624,12 +5629,10 @@ test_kyc (void)
                                                     instance.instance.id,
                                                     &account.h_wire,
                                                     "https://exchange.net/";,
-                                                    1LLU,
                                                     now,
                                                     MHD_HTTP_OK,
                                                     TALER_EC_NONE,
                                                     NULL,
-                                                    0,
                                                     NULL,
                                                     false,
                                                     false));
@@ -5638,12 +5641,10 @@ test_kyc (void)
                                                     instance.instance.id,
                                                     &account.h_wire,
                                                     "https://exchange2.com/";,
-                                                    1LLU,
                                                     now,
                                                     MHD_HTTP_OK,
                                                     TALER_EC_NONE,
                                                     NULL,
-                                                    0,
                                                     NULL,
                                                     false,
                                                     false));
@@ -5652,12 +5653,10 @@ test_kyc (void)
                                                     instance.instance.id,
                                                     &account.h_wire,
                                                     "https://exchange.net/";,
-                                                    1LLU,
                                                     now,
                                                     MHD_HTTP_OK,
                                                     TALER_EC_NONE,
                                                     NULL,
-                                                    0,
                                                     NULL,
                                                     false,
                                                     true));
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 7882f789..00fe71fe 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -117,6 +117,13 @@ struct TALER_MERCHANTDB_AccountDetails
    */
   struct TALER_WireSaltP salt;
 
+  /**
+   * Instance ID. Do not free (may be aliased with
+   * the instance ID given in the query!).
+   * FIXME: set in all functions involving this struct!
+   */
+  const char *instance_id;
+
   /**
    * Actual account address as a payto://-URI.
    */
@@ -257,11 +264,13 @@ typedef void
  * Callback invoked with information about a bank account.
  *
  * @param cls closure
+ * @param merchant_priv private key of the merchant instance
  * @param ad details about the account
  */
 typedef void
 (*TALER_MERCHANTDB_AccountCallback)(
   void *cls,
+  const struct TALER_MerchantPrivateKeyP *merchant_priv,
   const struct TALER_MERCHANTDB_AccountDetails *ad);
 
 
@@ -1463,14 +1472,12 @@ struct TALER_MERCHANTDB_Plugin
    * Insert information about an instance's account into our database.
    *
    * @param cls closure
-   * @param id identifier of the instance
    * @param account_details details about the account
    * @return database result code
    */
   enum GNUNET_DB_QueryStatus
   (*insert_account)(
     void *cls,
-    const char *id,
     const struct TALER_MERCHANTDB_AccountDetails *account_details);
 
 
@@ -1699,6 +1706,39 @@ struct TALER_MERCHANTDB_Plugin
     TALER_MERCHANTDB_KycCallback kyc_cb,
     void *kyc_cb_cls);
 
+  /**
+   * Check an account's KYC status at an exchange.
+   *
+   * @param cls closure
+   * @param merchant_payto_uri  merchant backend instance ID
+   * @param instance_id the instance for which to check
+   * @param exchange_url base URL of the exchange
+   * @param[out] auth_ok true if @a access_token was set
+   * @param[out] access_token set to access token for /kyc-info
+   * @param[out] kyc_ok true if no urgent KYC work must be done for this 
account
+   * @param[out] last_http_status set to last HTTP status from exchange on 
/kyc-check
+   * @param[out] last_ec set to last Taler error code from exchange on 
/kyc-check
+   * @param[out] last_kyc_check set to time of last KYC check
+   * @param[out] aml_review set to true if the account is under AML review (if 
this exposed)
+   * @param[out] jlimits set to JSON array with AccountLimits, NULL if unknown 
(and likely defaults apply or KYC auth is urgently needed, see @a auth_ok)
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*get_kyc_status)(
+    void *cls,
+    const char *merchant_account_uri,
+    const char *instance_id,
+    const char *exchange_url,
+    bool *auth_ok,
+    struct TALER_AccountAccessTokenP *access_token,
+    bool *kyc_ok,
+    unsigned int *last_http_status,
+    enum TALER_ErrorCode *last_ec,
+    struct GNUNET_TIME_Timestamp *last_kyc_check,
+    bool *aml_review,
+    json_t **jlimits);
+
+
   /**
    * Update an instance's account's KYC status.
    *
@@ -1706,13 +1746,11 @@ struct TALER_MERCHANTDB_Plugin
    * @param merchant_id merchant backend instance ID
    * @param h_wire hash of the wire account to check
    * @param exchange_url base URL of the exchange to check
-   * @param exchange_kyc_serial serial number for our account at the exchange 
(0 if unknown)
    * @param timestamp timestamp to store
    * @param exchange_http_status HTTP status code returned last by the exchange
    * @param exchange_ec_code Taler error code returned last by the exchange
    * @param access_token access token for the KYC process, NULL for none
-   * @param num_limits length of the @a limits array
-   * @param limits array with deposit limits returned by the exchange
+   * @param jlimits JSON array with AccountLimits returned by the exchange
    * @param in_aml_review true if the exchange says the account is under review
    * @param kyc_ok current KYC status (true for satisfied)
    * @return database result code
@@ -1723,13 +1761,11 @@ struct TALER_MERCHANTDB_Plugin
     const char *merchant_id,
     const struct TALER_MerchantWireHashP *h_wire,
     const char *exchange_url,
-    uint64_t exchange_kyc_serial,
     struct GNUNET_TIME_Timestamp timestamp,
     unsigned int exchange_http_status,
     enum TALER_ErrorCode exchange_ec_code,
     const struct TALER_AccountAccessTokenP *access_token,
-    unsigned int num_limits,
-    const struct TALER_MERCHANTDB_DepositLimits *limits,
+    const json_t *jlimits,
     bool in_aml_review,
     bool kyc_ok);
 

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