gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: policy extensions and age restri


From: gnunet
Subject: [taler-exchange] branch master updated: policy extensions and age restriction refactoring
Date: Fri, 04 Nov 2022 12:20:19 +0100

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

oec pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 752f1027 policy extensions and age restriction refactoring
752f1027 is described below

commit 752f10273860d2496fc3eb1e03de6ad4451e7c0f
Author: Özgür Kesim <oec-taler@kesim.org>
AuthorDate: Fri Nov 4 12:18:16 2022 +0100

    policy extensions and age restriction refactoring
    
    - refactoring of extension-plugin-mechanism
    - refactoring of age restriction extension
    - added policy extensions plugin plumbing
    - added DB schema and api
      - policy_details
      - policy_fulfillments
---
 configure.ac                                       |   1 +
 src/Makefile.am                                    |   1 +
 .../taler-auditor-httpd_deposit-confirmation.c     |   6 +-
 src/auditor/taler-auditor-sync.c                   |   3 +-
 src/auditor/taler-helper-auditor-coins.c           |   2 +-
 src/auditor/test-auditor.sh                        |   8 +-
 src/auditor/test-revocation.sh                     |   8 +-
 src/auditor/test-sync.sh                           |   6 +-
 src/auditordb/auditor-0001.sql                     |   2 +-
 src/auditordb/pg_get_deposit_confirmations.c       |   6 +-
 src/auditordb/pg_insert_deposit_confirmation.c     |   4 +-
 src/exchange-tools/taler-exchange-offline.c        | 123 ++++---
 src/exchange/taler-exchange-httpd.c                |  14 +-
 src/exchange/taler-exchange-httpd.h                |  10 +-
 src/exchange/taler-exchange-httpd_batch-deposit.c  | 121 ++++--
 src/exchange/taler-exchange-httpd_deposit.c        | 102 ++++-
 src/exchange/taler-exchange-httpd_extensions.c     | 266 ++++++++++++--
 src/exchange/taler-exchange-httpd_extensions.h     |  15 +
 src/exchange/taler-exchange-httpd_keys.c           |  85 +++--
 .../taler-exchange-httpd_management_extensions.c   |  48 +--
 src/exchange/taler-exchange-httpd_metrics.h        |   3 +-
 .../taler-exchange-httpd_refreshes_reveal.c        |   5 +-
 src/exchange/taler-exchange-httpd_responses.c      |   2 +-
 src/exchangedb/common-0001.sql                     |   6 +-
 src/exchangedb/exchange-0001-part.sql              | 100 +++--
 src/exchangedb/pg_insert_records_by_table.c        | 114 ++++--
 src/exchangedb/pg_lookup_records_by_table.c        | 131 +++++--
 src/exchangedb/pg_lookup_serial_by_table.c         |  18 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 278 ++++++++++++--
 src/exchangedb/procedures.sql                      | 139 ++++++-
 src/exchangedb/test_exchangedb.c                   |  58 +--
 src/extensions/Makefile.am                         |   5 +-
 src/extensions/{ => age_restriction}/Makefile.am   |  16 +-
 src/extensions/age_restriction/age_restriction.c   | 258 +++++++++++++
 src/extensions/age_restriction_helper.c            |  73 ++++
 src/extensions/extension_age_restriction.c         | 409 ---------------------
 src/extensions/extensions.c                        | 231 ++++++++----
 src/include/taler_auditor_service.h                |   4 +-
 src/include/taler_auditordb_plugin.h               |   4 +-
 src/include/taler_crypto_lib.h                     |  43 ++-
 src/include/taler_exchange_service.h               |   4 +-
 src/include/taler_exchangedb_plugin.h              | 135 +++++--
 src/include/taler_extensions.h                     | 345 ++++++++++-------
 src/include/taler_extensions_policy.h              | 198 ++++++++++
 src/include/taler_json_lib.h                       |  20 +-
 src/include/taler_util.h                           |  58 +++
 src/json/json.c                                    |  16 +-
 src/lib/auditor_api_deposit_confirmation.c         |  14 +-
 src/lib/exchange_api_batch_deposit.c               |  18 +-
 src/lib/exchange_api_common.c                      |  11 +-
 src/lib/exchange_api_common.h                      |   4 +-
 src/lib/exchange_api_deposit.c                     |  16 +-
 src/lib/exchange_api_handle.c                      |  26 +-
 src/lib/exchange_api_refund.c                      |  13 +-
 src/testing/test_exchange_api.c                    |   3 -
 src/testing/test_exchange_p2p.c                    |   3 -
 .../testing_api_cmd_auditor_deposit_confirmation.c |   4 +-
 src/testing/testing_api_cmd_batch_deposit.c        |   2 +-
 src/testing/testing_api_cmd_batch_withdraw.c       |   2 +-
 src/testing/testing_api_cmd_deposit.c              |   2 +-
 src/testing/testing_api_cmd_withdraw.c             |   2 +-
 src/testing/testing_api_helpers_exchange.c         |   9 +-
 src/util/Makefile.am                               |   1 +
 src/util/age_restriction.c                         | 111 ++++++
 src/util/exchange_signatures.c                     |  18 +-
 src/util/offline_signatures.c                      |  26 +-
 src/util/wallet_signatures.c                       |  18 +-
 67 files changed, 2627 insertions(+), 1180 deletions(-)

diff --git a/configure.ac b/configure.ac
index a35da334..2df8674e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -532,6 +532,7 @@ AC_CONFIG_FILES([Makefile
                  src/exchangedb/Makefile
                  src/exchange-tools/Makefile
                  src/extensions/Makefile
+                 src/extensions/age_restriction/Makefile
                  src/lib/Makefile
                  src/kyclogic/Makefile
                  src/testing/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fa748d7..e10ecf8d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,5 +32,6 @@ SUBDIRS = \
   auditor \
   lib \
   exchange-tools \
+  extensions/age_restriction \
   testing \
   benchmark
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c 
b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index f4d89b7c..c7bb4f50 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -227,7 +227,7 @@ verify_and_execute_deposit_confirmation (
       TALER_exchange_online_deposit_confirmation_verify (
         &dc->h_contract_terms,
         &dc->h_wire,
-        NULL /* h_extensions! */,
+        &dc->h_policy,
         dc->exchange_timestamp,
         dc->wire_deadline,
         dc->refund_deadline,
@@ -276,8 +276,8 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler 
*rh,
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
                                  &dc.h_contract_terms),
-    GNUNET_JSON_spec_fixed_auto ("h_extensions",
-                                 &dc.h_extensions),
+    GNUNET_JSON_spec_fixed_auto ("h_policy",
+                                 &dc.h_policy),
     GNUNET_JSON_spec_fixed_auto ("h_wire",
                                  &dc.h_wire),
     GNUNET_JSON_spec_timestamp ("exchange_timestamp",
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
index 3c4c7e4c..a7ccf800 100644
--- a/src/auditor/taler-auditor-sync.c
+++ b/src/auditor/taler-auditor-sync.c
@@ -117,7 +117,8 @@ static struct Table tables[] = {
   { .rt = TALER_EXCHANGEDB_RT_RECOUP},
   { .rt = TALER_EXCHANGEDB_RT_RECOUP_REFRESH },
   { .rt = TALER_EXCHANGEDB_RT_EXTENSIONS},
-  { .rt = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS },
+  { .rt = TALER_EXCHANGEDB_RT_POLICY_DETAILS },
+  { .rt = TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS },
   { .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
   { .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION},
   { .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES},
diff --git a/src/auditor/taler-helper-auditor-coins.c 
b/src/auditor/taler-helper-auditor-coins.c
index da0ec99e..7637e463 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -1566,7 +1566,7 @@ deposit_cb (void *cls,
                                      &h_wire,
                                      &deposit->h_contract_terms,
                                      &deposit->coin.h_age_commitment,
-                                     NULL /* FIXME-Oec: #7270: h_extensions! 
*/,
+                                     &deposit->h_policy,
                                      &h_denom_pub,
                                      deposit->timestamp,
                                      &deposit->merchant_pub,
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index 34a3980d..0390c206 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -87,7 +87,7 @@ function stop_libeufin()
 # Cleanup exchange and libeufin between runs.
 function cleanup()
 {
-    if test ! -z ${EPID:-}
+    if test ! -z "${EPID:-}"
     then
         echo -n "Stopping exchange $EPID..."
         kill -TERM $EPID
@@ -102,7 +102,7 @@ function cleanup()
 function exit_cleanup()
 {
     echo "Running exit-cleanup"
-    if test ! -z ${POSTGRES_PATH:-}
+    if test ! -z "${POSTGRES_PATH:-}"
     then
         echo "Stopping Postgres at ${POSTGRES_PATH}"
         ${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || 
true
@@ -2047,10 +2047,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null 
|| exit_skip "taler-wallet
 echo -n "Testing for Postgres"
 # Available directly in path?
 INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
   echo " FOUND (in path) at" $INITDB_BIN
 else
-  HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || 
exit_skip " MISSING"
+  HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep 
postgres` || exit_skip " MISSING"
   echo " FOUND at" `dirname $HAVE_INITDB`
   INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | 
tail -n1`
 fi
diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh
index 22d1c86a..2751b5c2 100755
--- a/src/auditor/test-revocation.sh
+++ b/src/auditor/test-revocation.sh
@@ -81,7 +81,7 @@ function stop_libeufin()
 # Cleanup to run whenever we exit
 function cleanup()
 {
-    if test ! -z ${EPID:-}
+    if test ! -z "${EPID:-}"
     then
         echo -n "Stopping exchange $EPID..."
         kill -TERM $EPID
@@ -96,7 +96,7 @@ function cleanup()
 function exit_cleanup()
 {
     echo "Running exit-cleanup"
-    if test ! -z ${POSTGRES_PATH:-}
+    if test -z "${POSTGRES_PATH:-}"
     then
         echo "Stopping Postgres at ${POSTGRES_PATH}"
         ${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || 
true
@@ -640,10 +640,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || 
exit_skip "taler-wallet
 echo -n "Testing for Postgres"
 # Available directly in path?
 INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
   echo " FOUND (in path) at" $INITDB_BIN
 else
-  HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || 
exit_skip " MISSING"
+  HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep 
postgres` || exit_skip " MISSING"
   echo " FOUND at" `dirname $HAVE_INITDB`
   INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | 
tail -n1`
 fi
diff --git a/src/auditor/test-sync.sh b/src/auditor/test-sync.sh
index 174e03c6..92fe949f 100755
--- a/src/auditor/test-sync.sh
+++ b/src/auditor/test-sync.sh
@@ -32,7 +32,7 @@ function exit_fail() {
 
 # Cleanup to run whenever we exit
 function cleanup() {
-    if test ! -z ${POSTGRES_PATH:-}
+    if test ! -z "${POSTGRES_PATH:-}"
     then
         ${POSTGRES_PATH}/pg_ctl -D $TMPDIR stop &> /dev/null || true
     fi
@@ -111,10 +111,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || 
exit_skip "taler-wallet
 echo -n "Testing for Postgres"
 # Available directly in path?
 INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
   echo " FOUND (in path) at" $INITDB_BIN
 else
-  HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || 
exit_skip " MISSING"
+  HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep 
postgres` || exit_skip " MISSING"
   echo " FOUND at" `dirname $HAVE_INITDB`
   INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | 
tail -n1`
 fi
diff --git a/src/auditordb/auditor-0001.sql b/src/auditordb/auditor-0001.sql
index a19655d4..1fdbe78c 100644
--- a/src/auditordb/auditor-0001.sql
+++ b/src/auditordb/auditor-0001.sql
@@ -315,7 +315,7 @@ CREATE TABLE IF NOT EXISTS deposit_confirmations
   (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES 
auditor_exchanges(master_pub) ON DELETE CASCADE
   ,serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
   ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
-  ,h_extensions BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
+  ,h_policy BYTEA NOT NULL CHECK (LENGTH(h_policy)=64)
   ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)
   ,exchange_timestamp INT8 NOT NULL
   ,refund_deadline INT8 NOT NULL
diff --git a/src/auditordb/pg_get_deposit_confirmations.c 
b/src/auditordb/pg_get_deposit_confirmations.c
index 6c7fe73c..3f0bd1e2 100644
--- a/src/auditordb/pg_get_deposit_confirmations.c
+++ b/src/auditordb/pg_get_deposit_confirmations.c
@@ -87,8 +87,8 @@ deposit_confirmation_cb (void *cls,
                                     &serial_id),
       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
                                             &dc.h_contract_terms),
-      GNUNET_PQ_result_spec_auto_from_type ("h_extensions",
-                                            &dc.h_extensions),
+      GNUNET_PQ_result_spec_auto_from_type ("h_policy",
+                                            &dc.h_policy),
       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
                                             &dc.h_wire),
       GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
@@ -158,7 +158,7 @@ TAH_PG_get_deposit_confirmations (
            "SELECT"
            " serial_id"
            ",h_contract_terms"
-           ",h_extensions"
+           ",h_policy"
            ",h_wire"
            ",exchange_timestamp"
            ",wire_deadline"
diff --git a/src/auditordb/pg_insert_deposit_confirmation.c 
b/src/auditordb/pg_insert_deposit_confirmation.c
index cc52ba4e..675f8ed0 100644
--- a/src/auditordb/pg_insert_deposit_confirmation.c
+++ b/src/auditordb/pg_insert_deposit_confirmation.c
@@ -35,7 +35,7 @@ TAH_PG_insert_deposit_confirmation (
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (&dc->master_public_key),
     GNUNET_PQ_query_param_auto_from_type (&dc->h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (&dc->h_extensions),
+    GNUNET_PQ_query_param_auto_from_type (&dc->h_policy),
     GNUNET_PQ_query_param_auto_from_type (&dc->h_wire),
     GNUNET_PQ_query_param_timestamp (&dc->exchange_timestamp),
     GNUNET_PQ_query_param_timestamp (&dc->wire_deadline),
@@ -54,7 +54,7 @@ TAH_PG_insert_deposit_confirmation (
            "INSERT INTO deposit_confirmations "
            "(master_pub"
            ",h_contract_terms"
-           ",h_extensions"
+           ",h_policy"
            ",h_wire"
            ",exchange_timestamp"
            ",wire_deadline"
diff --git a/src/exchange-tools/taler-exchange-offline.c 
b/src/exchange-tools/taler-exchange-offline.c
index 26ddf738..a8a25bd3 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -137,6 +137,12 @@ static struct GNUNET_CURL_RescheduleContext *rc;
  */
 static const struct GNUNET_CONFIGURATION_Handle *kcfg;
 
+/**
+ * Age restriction configuration
+ */
+static bool ar_enabled = false;
+static struct TALER_AgeRestrictionConfig ar_config = {0};
+
 /**
  * Return value from main().
  */
@@ -163,11 +169,6 @@ static char *currency;
  */
 static char *CFG_exchange_url;
 
-/**
- * If age restriction is enabled, the age mask to be used
- */
-static struct TALER_AgeMask age_mask = {0};
-
 /**
  * A subcommand supported by this program.
  */
@@ -2159,14 +2160,14 @@ upload_extensions (const char *exchange_url,
 
   /* 2. Verify the signature */
   {
-    struct TALER_ExtensionConfigHashP h_config;
+    struct TALER_ExtensionManifestsHashP h_manifests;
 
     if (GNUNET_OK !=
-        TALER_JSON_extensions_config_hash (extensions, &h_config))
+        TALER_JSON_extensions_manifests_hash (extensions, &h_manifests))
     {
       GNUNET_JSON_parse_free (spec);
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "couldn't hash extensions\n");
+                  "couldn't hash extensions' manifests\n");
       global_ret = EXIT_FAILURE;
       test_shutdown ();
       return;
@@ -2176,8 +2177,8 @@ upload_extensions (const char *exchange_url,
         load_offline_key (GNUNET_NO))
       return;
 
-    if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
-          &h_config,
+    if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify (
+          &h_manifests,
           &master_pub,
           &sig))
     {
@@ -3858,7 +3859,7 @@ load_age_mask (const char*section_name)
   static const struct TALER_AgeMask null_mask = {0};
   enum GNUNET_GenericReturnValue ret;
 
-  if (age_mask.bits == 0)
+  if (! ar_enabled)
     return null_mask;
 
   if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
@@ -3870,14 +3871,14 @@ load_age_mask (const char*section_name)
   ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
                                               section_name,
                                               "AGE_RESTRICTED");
-  if (GNUNET_YES == ret)
-    return age_mask;
-
   if (GNUNET_SYSERR == ret)
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
                                section_name,
                                "AGE_RESTRICTED",
                                "Value must be YES or NO\n");
+  if (GNUNET_YES == ret)
+    return ar_config.mask;
+
   return null_mask;
 }
 
@@ -4218,18 +4219,24 @@ do_setup (char *const *args)
 static void
 do_extensions_show (char *const *args)
 {
-  const struct TALER_Extension *it;
+  const struct TALER_Extensions *it;
   json_t *exts = json_object ();
   json_t *obj;
 
   GNUNET_assert (NULL != exts);
   for (it = TALER_extensions_get_head ();
-       NULL != it;
+       NULL != it && NULL != it->extension;
        it = it->next)
-    GNUNET_assert (0 ==
-                   json_object_set_new (exts,
-                                        it->name,
-                                        it->config_to_json (it)));
+  {
+    const struct TALER_Extension *extension = it->extension;
+    int ret;
+
+    ret = json_object_set_new (exts,
+                               extension->name,
+                               extension->manifest (extension));
+    GNUNET_assert (-1 != ret);
+  }
+
   obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_object_steal ("extensions",
                                    exts));
@@ -4238,7 +4245,7 @@ do_extensions_show (char *const *args)
               json_dumps (obj,
                           JSON_INDENT (2)));
   json_decref (obj);
-  next (args + 1);
+  next (args);
 }
 
 
@@ -4249,35 +4256,38 @@ static void
 do_extensions_sign (char *const *args)
 {
   json_t *extensions = json_object ();
-  struct TALER_ExtensionConfigHashP h_config;
+  struct TALER_ExtensionManifestsHashP h_manifests;
   struct TALER_MasterSignatureP sig;
-  const struct TALER_Extension *it;
+  const struct TALER_Extensions *it;
+  bool found = false;
   json_t *obj;
 
   GNUNET_assert (NULL != extensions);
-  if (GNUNET_OK !=
-      TALER_extensions_load_taler_config (kcfg))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "error while loading taler config for extensions\n");
-    json_decref (extensions);
-    return;
-  }
   for (it = TALER_extensions_get_head ();
-       NULL != it;
+       NULL != it && NULL != it->extension;
        it = it->next)
+  {
+    const struct TALER_Extension *ext = it->extension;
+    GNUNET_assert (ext);
+
+    found = true;
+
     GNUNET_assert (0 ==
                    json_object_set_new (extensions,
-                                        it->name,
-                                        it->config_to_json (it)));
+                                        ext->name,
+                                        ext->manifest (ext)));
+  }
+
+  if (! found)
+    return;
 
   if (GNUNET_OK !=
-      TALER_JSON_extensions_config_hash (extensions,
-                                         &h_config))
+      TALER_JSON_extensions_manifests_hash (extensions,
+                                            &h_manifests))
   {
     json_decref (extensions);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "error while hashing config for extensions\n");
+                "error while hashing manifest for extensions\n");
     return;
   }
 
@@ -4288,18 +4298,19 @@ do_extensions_sign (char *const *args)
     return;
   }
 
-  TALER_exchange_offline_extension_config_hash_sign (&h_config,
-                                                     &master_priv,
-                                                     &sig);
+  TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests,
+                                                        &master_priv,
+                                                        &sig);
   obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_object_steal ("extensions",
                                    extensions),
     GNUNET_JSON_pack_data_auto (
       "extensions_sig",
       &sig));
+
   output_operation (OP_EXTENSIONS,
                     obj);
-  next (args + 1);
+  next (args);
 }
 
 
@@ -4500,25 +4511,31 @@ run (void *cls,
   (void) cls;
   (void) cfgfile;
   kcfg = cfg;
-  if (GNUNET_OK !=
-      TALER_config_get_currency (kcfg,
-                                 &currency))
+
+  /* load extensions */
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_extensions_init (kcfg));
+
+  /* setup age restriction, if applicable */
   {
-    global_ret = EXIT_NOTCONFIGURED;
-    return;
+    const struct TALER_AgeRestrictionConfig *arc;
+
+    if (NULL !=
+        (arc = TALER_extensions_get_age_restriction_config ()))
+    {
+      ar_config  = *arc;
+      ar_enabled = true;
+    }
   }
 
-  /* load age mask, if age restriction is enabled */
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_extension_age_restriction_register ());
 
-  if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
+  if (GNUNET_OK !=
+      TALER_config_get_currency (kcfg,
+                                 &currency))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "error while loading taler config for extensions\n");
+    global_ret = EXIT_NOTCONFIGURED;
     return;
   }
-  age_mask = TALER_extensions_age_restriction_ageMask ();
 
   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
                           &rc);
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 34f93879..691e1ef7 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -101,6 +101,14 @@ static int allow_address_reuse;
  */
 const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
 
+/**
+ * Configuration of age restriction
+ *
+ * Set after loading the library, enabled in database event handler.
+ */
+bool TEH_age_restriction_enabled = false;
+struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0};
+
 /**
  * Handle to the HTTP server.
  */
@@ -142,11 +150,6 @@ char *TEH_currency;
  */
 char *TEH_base_url;
 
-/**
- * Age restriction flags and mask
- */
-bool TEH_age_restriction_enabled = true;
-
 /**
  * Default timeout in seconds for HTTP requests.
  */
@@ -174,6 +177,7 @@ bool TEH_suicide;
  * TALER_SIGNATURE_MASTER_EXTENSION.
  */
 struct TALER_MasterSignatureP TEH_extensions_sig;
+bool TEH_extensions_signed = false;
 
 /**
  * Value to return from main()
diff --git a/src/exchange/taler-exchange-httpd.h 
b/src/exchange/taler-exchange-httpd.h
index 0fda5ed8..4d3fb490 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
  */
 extern char *TEH_currency;
 
-/*
- * Age restriction extension state
- */
-extern bool TEH_age_restriction_enabled;
-
 /**
  * Our (externally visible) base URL.
  */
@@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
  * Signature of the offline master key of all enabled extensions' configuration
  */
 extern struct TALER_MasterSignatureP TEH_extensions_sig;
+extern bool TEH_extensions_signed;
 
 /**
  * @brief Struct describing an URL and the handler for it.
@@ -366,4 +362,8 @@ struct TEH_RequestHandler
 };
 
 
+/* Age restriction configuration */
+extern bool TEH_age_restriction_enabled;
+extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config;
+
 #endif
diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c 
b/src/exchange/taler-exchange-httpd_batch-deposit.c
index c2a9cbd5..4d4197ab 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -87,15 +87,27 @@ struct BatchDepositContext
   const char *payto_uri;
 
   /**
-   * Additional details for extensions relevant for this
+   * Additional details for policy extension relevant for this
    * deposit operation, possibly NULL!
    */
-  json_t *extension_details;
+  json_t *policy_json;
 
   /**
-   * Hash over @e extension_details.
+   * Will be true if policy_json were provided
    */
-  struct TALER_ExtensionContractHashP h_extensions;
+  bool has_policy;
+
+  /**
+   * If @e policy_json was present, the corresponding policy extension
+   * calculates these details.  These will be persisted in the policy_details
+   * table.
+   */
+  struct TALER_PolicyDetails policy_details;
+
+  /**
+   * Hash over @e policy_details.
+   */
+  struct TALER_ExtensionPolicyHashP h_policy;
 
   /**
    * Time when this request was generated.  Used, for example, to
@@ -173,7 +185,7 @@ again:
            &TEH_keys_exchange_sign_,
            &bdc->h_contract_terms,
            &bdc->h_wire,
-           &bdc->h_extensions,
+           bdc->has_policy ? &bdc->h_policy: NULL,
            bdc->exchange_timestamp,
            bdc->wire_deadline,
            bdc->refund_deadline,
@@ -242,7 +254,7 @@ batch_deposit_transaction (void *cls,
                            MHD_RESULT *mhd_ret)
 {
   struct BatchDepositContext *dc = cls;
-  enum GNUNET_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs = GNUNET_SYSERR;
   bool balance_ok;
   bool in_conflict;
 
@@ -469,18 +481,19 @@ parse_coin (struct MHD_Connection *connection,
 
   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
   if (GNUNET_OK !=
-      TALER_wallet_deposit_verify (&deposit->amount_with_fee,
-                                   &deposit->deposit_fee,
-                                   &dc->h_wire,
-                                   &dc->h_contract_terms,
-                                   &deposit->coin.h_age_commitment,
-                                   &dc->h_extensions,
-                                   &deposit->coin.denom_pub_hash,
-                                   dc->timestamp,
-                                   &dc->merchant_pub,
-                                   dc->refund_deadline,
-                                   &deposit->coin.coin_pub,
-                                   &deposit->csig))
+      TALER_wallet_deposit_verify (
+        &deposit->amount_with_fee,
+        &deposit->deposit_fee,
+        &dc->h_wire,
+        &dc->h_contract_terms,
+        &deposit->coin.h_age_commitment,
+        dc->has_policy ? &dc->h_policy : NULL,
+        &deposit->coin.denom_pub_hash,
+        dc->timestamp,
+        &dc->merchant_pub,
+        dc->refund_deadline,
+        &deposit->coin.coin_pub,
+        &deposit->csig))
   {
     TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
     GNUNET_JSON_parse_free (spec);
@@ -496,11 +509,6 @@ parse_coin (struct MHD_Connection *connection,
   deposit->h_contract_terms = dc->h_contract_terms;
   deposit->wire_salt = dc->wire_salt;
   deposit->receiver_wire_account = (char *) dc->payto_uri;
-  /* FIXME-OEC: #7270 should NOT insert the extension details N times,
-     but rather insert them ONCE and then per-coin only use
-     the resulting extension UUID/serial; so the data structure
-     here should be changed once we look at extensions in earnest.  */
-  deposit->extension_details = dc->extension_details;
   deposit->timestamp = dc->timestamp;
   deposit->refund_deadline = dc->refund_deadline;
   deposit->wire_deadline = dc->wire_deadline;
@@ -517,7 +525,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
   struct BatchDepositContext dc;
   json_t *coins;
   bool no_refund_deadline = true;
-  bool no_extensions = true;
+  bool no_policy_json = true;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("merchant_payto_uri",
                              &dc.payto_uri),
@@ -530,9 +538,9 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
     GNUNET_JSON_spec_json ("coins",
                            &coins),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("extension_details",
-                             &dc.extension_details),
-      &no_extensions),
+      GNUNET_JSON_spec_json ("policy",
+                             &dc.policy_json),
+      &no_policy_json),
     GNUNET_JSON_spec_timestamp ("timestamp",
                                 &dc.timestamp),
     GNUNET_JSON_spec_mark_optional (
@@ -563,6 +571,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
     return MHD_YES;   /* failure */
   }
 
+  dc.has_policy = ! no_policy_json;
+
   /* validate merchant's wire details (as far as we can) */
   {
     char *emsg;
@@ -607,11 +617,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
   TALER_merchant_wire_signature_hash (dc.payto_uri,
                                       &dc.wire_salt,
                                       &dc.h_wire);
-  /* FIXME-OEC: #7270 hash actual extension JSON object here */
-  // if (! no_extensions)
-  memset (&dc.h_extensions,
-          0,
-          sizeof (dc.h_extensions));
+
+  /* handle policy, if present */
+  if (dc.has_policy)
+  {
+    const char *error_hint = NULL;
+
+    if (GNUNET_OK !=
+        TALER_extensions_create_policy_details (
+          dc.policy_json,
+          &dc.policy_details,
+          &error_hint))
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+                                         error_hint);
+
+    TALER_deposit_policy_hash (dc.policy_json,
+                               &dc.h_policy);
+  }
+
   dc.num_coins = json_array_size (coins);
   if (0 == dc.num_coins)
   {
@@ -635,12 +660,32 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
                                   struct TALER_EXCHANGEDB_Deposit);
   for (unsigned int i = 0; i<dc.num_coins; i++)
   {
-    if (GNUNET_OK !=
-        (res = parse_coin (connection,
-                           json_array_get (coins,
-                                           i),
-                           &dc,
-                           &dc.deposits[i])))
+    do {
+      res = parse_coin (connection,
+                        json_array_get (coins, i),
+                        &dc,
+                        &dc.deposits[i]);
+      if (GNUNET_OK != res)
+        break;
+
+      /* If applicable, accumulate all contributions into the policy_details */
+      if (dc.has_policy)
+      {
+        /* FIXME: how do deposit-fee and policy-fee interact? */
+        struct TALER_Amount amount_without_fee;
+
+        res = TALER_amount_subtract (&amount_without_fee,
+                                     &dc.deposits[i].amount_with_fee,
+                                     &dc.deposits[i].deposit_fee
+                                     );
+        res = TALER_amount_add (
+          &dc.policy_details.accumulated_total,
+          &dc.policy_details.accumulated_total,
+          &amount_without_fee);
+      }
+    } while(0);
+
+    if (GNUNET_OK != res)
     {
       for (unsigned int j = 0; j<i; j++)
         TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 0484ab07..455888a8 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -21,6 +21,7 @@
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
+ * @author Özgür Kesim
  */
 #include "platform.h"
 #include <gnunet/gnunet_util_lib.h>
@@ -47,7 +48,7 @@
  * @param connection connection to the client
  * @param coin_pub public key of the coin
  * @param h_wire hash of wire details
- * @param h_extensions hash of applicable extensions
+ * @param h_policy hash of applicable policy extension
  * @param h_contract_terms hash of contract details
  * @param exchange_timestamp exchange's timestamp
  * @param refund_deadline until when this deposit be refunded
@@ -61,7 +62,7 @@ reply_deposit_success (
   struct MHD_Connection *connection,
   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp refund_deadline,
@@ -78,7 +79,7 @@ reply_deposit_success (
          &TEH_keys_exchange_sign_,
          h_contract_terms,
          h_wire,
-         h_extensions,
+         h_policy,
          exchange_timestamp,
          wire_deadline,
          refund_deadline,
@@ -131,6 +132,29 @@ struct DepositContext
    */
   uint64_t known_coin_id;
 
+  /*
+   * True if @e policy_json was provided
+   */
+  bool has_policy;
+
+  /**
+   * If @e has_policy is true, the corresponding policy extension calculates
+   * these details.  These will be persisted in the policy_details table.
+   */
+  struct TALER_PolicyDetails policy_details;
+
+  /**
+   * Hash over the policy data for this deposit (remains unknown to the
+   * Exchange).  Needed for the verification of the deposit's signature
+   */
+  struct TALER_ExtensionPolicyHashP h_policy;
+
+  /**
+   * When has_policy is true, and deposit->policy_details are
+   * persisted, this contains the id of the record in the policy_details table.
+   */
+  uint64_t policy_details_serial_id;
+
 };
 
 
@@ -163,14 +187,35 @@ deposit_transaction (void *cls,
                             mhd_ret);
   if (qs < 0)
     return qs;
-  qs = TEH_plugin->do_deposit (TEH_plugin->cls,
-                               dc->deposit,
-                               dc->known_coin_id,
-                               &dc->h_payto,
-                               false, /* FIXME-OEC: extension blocked #7270 */
-                               &dc->exchange_timestamp,
-                               &balance_ok,
-                               &in_conflict);
+
+
+  /* If the deposit has a policy associated to it, persist it.  This will
+   * insert or update the record. */
+  if (dc->has_policy)
+  {
+    qs = TEH_plugin->persist_policy_details (
+      TEH_plugin->cls,
+      &dc->policy_details,
+      &dc->policy_details_serial_id,
+      &dc->policy_details.accumulated_total,
+      &dc->policy_details.fulfillment_state);
+
+    if (qs < 0)
+      return qs;
+  }
+
+
+  qs = TEH_plugin->do_deposit (
+    TEH_plugin->cls,
+    dc->deposit,
+    dc->known_coin_id,
+    &dc->h_payto,
+    (dc->has_policy)
+             ? &dc->policy_details_serial_id
+             : NULL,
+    &dc->exchange_timestamp,
+    &balance_ok,
+    &in_conflict);
   if (qs < 0)
   {
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -216,6 +261,8 @@ TEH_handler_deposit (struct MHD_Connection *connection,
   struct DepositContext dc;
   struct TALER_EXCHANGEDB_Deposit deposit;
   const char *payto_uri;
+  struct TALER_ExtensionPolicyHashP *ph_policy = NULL;
+  bool no_policy_json;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("merchant_payto_uri",
                              &payto_uri),
@@ -240,12 +287,18 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                  &deposit.csig),
     GNUNET_JSON_spec_timestamp ("timestamp",
                                 &deposit.timestamp),
+    /* TODO: refund_deadline and merchant_pub will move into the
+     * extension policy_merchant_refunds */
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_timestamp ("refund_deadline",
                                   &deposit.refund_deadline),
       NULL),
     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
                                 &deposit.wire_deadline),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_json ("policy",
+                             &dc.policy_details.policy_json),
+      &no_policy_json),
     GNUNET_JSON_spec_end ()
   };
   struct TALER_MerchantWireHashP h_wire;
@@ -271,6 +324,9 @@ TEH_handler_deposit (struct MHD_Connection *connection,
       return MHD_YES; /* failure */
     }
   }
+
+  dc.has_policy = ! no_policy_json;
+
   /* validate merchant's wire details (as far as we can) */
   {
     char *emsg;
@@ -419,6 +475,26 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                        NULL);
   }
 
+  /* Check policy input and create policy details */
+  if (dc.has_policy)
+  {
+    const char *error_hint = NULL;
+
+    if (GNUNET_OK !=
+        TALER_extensions_create_policy_details (
+          dc.policy_details.policy_json,
+          &dc.policy_details,
+          &error_hint))
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+                                         error_hint);
+
+    TALER_deposit_policy_hash (dc.policy_details.policy_json,
+                               &dc.h_policy);
+    ph_policy = &dc.h_policy;
+  }
+
   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
   if (GNUNET_OK !=
       TALER_wallet_deposit_verify (&deposit.amount_with_fee,
@@ -426,7 +502,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                    &h_wire,
                                    &deposit.h_contract_terms,
                                    &deposit.coin.h_age_commitment,
-                                   NULL /* FIXME: h_extensions! */,
+                                   ph_policy,
                                    &deposit.coin.denom_pub_hash,
                                    deposit.timestamp,
                                    &deposit.merchant_pub,
@@ -481,7 +557,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
     res = reply_deposit_success (connection,
                                  &deposit.coin.coin_pub,
                                  &h_wire,
-                                 NULL /* FIXME: h_extensions! */,
+                                 ph_policy,
                                  &deposit.h_contract_terms,
                                  dc.exchange_timestamp,
                                  deposit.refund_deadline,
diff --git a/src/exchange/taler-exchange-httpd_extensions.c 
b/src/exchange/taler-exchange-httpd_extensions.c
index d6c26f6f..30d1c5ac 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -14,7 +14,7 @@
 */
 /**
  * @file taler-exchange-httpd_extensions.c
- * @brief Handle extensions (age-restriction, peer2peer)
+ * @brief Handle extensions (age-restriction, policy extensions)
  * @author Özgür Kesim
  */
 #include "platform.h"
@@ -77,65 +77,84 @@ extension_update_event_cb (void *cls,
     return;
   }
 
-  // Get the config from the database as string
+  // Get the manifest from the database as string
   {
-    char *config_str = NULL;
+    char *manifest_str = NULL;
     enum GNUNET_DB_QueryStatus qs;
     json_error_t err;
-    json_t *config;
+    json_t *manifest_js;
     enum GNUNET_GenericReturnValue ret;
 
-    qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
-                                           extension->name,
-                                           &config_str);
+    qs = TEH_plugin->get_extension_manifest (TEH_plugin->cls,
+                                             extension->name,
+                                             &manifest_str);
 
     if (qs < 0)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Couldn't get extension config\n");
+                  "Couldn't get extension manifest\n");
       GNUNET_break (0);
       return;
     }
 
     // No config found -> disable extension
-    if (NULL == config_str)
+    if (NULL == manifest_str)
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "No manifest found for extension %s, disabling it\n",
+                  extension->name);
       extension->disable ((struct TALER_Extension *) extension);
       return;
     }
 
     // Parse the string as JSON
-    config = json_loads (config_str, JSON_DECODE_ANY, &err);
-    if (NULL == config)
+    manifest_js = json_loads (manifest_str, JSON_DECODE_ANY, &err);
+    if (NULL == manifest_js)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to parse config for extension `%s' as JSON: %s 
(%s)\n",
+                  "Failed to parse manifest for extension `%s' as JSON: %s 
(%s)\n",
                   extension->name,
                   err.text,
                   err.source);
       GNUNET_break (0);
+      free (manifest_js);
       return;
     }
 
     // Call the parser for the extension
-    ret = extension->load_json_config (
+    ret = extension->load_config (
       (struct TALER_Extension *) extension,
-      config);
+      json_object_get (manifest_js, "config"));
 
     if (GNUNET_OK != ret)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Couldn't parse configuration for extension %s from the 
database",
-                  extension->name);
+                  "Couldn't parse configuration for extension %s from the 
manifest in the database: %s\n",
+                  extension->name,
+                  manifest_str);
       GNUNET_break (0);
     }
+
+    free (manifest_str);
+    json_decref (manifest_js);
   }
 
   /* Special case age restriction: Update global flag and mask  */
   if (TALER_Extension_AgeRestriction == type)
   {
-    TEH_age_restriction_enabled =
-      TALER_extensions_age_restriction_is_enabled ();
+    const struct TALER_AgeRestrictionConfig *conf =
+      TALER_extensions_get_age_restriction_config ();
+    TEH_age_restriction_enabled = false;
+    if (NULL != conf)
+    {
+      TEH_age_restriction_enabled = true;
+      TEH_age_restriction_config = *conf;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "[age restriction] DB event has changed the config to %s with 
mask: %s\n",
+                TEH_age_restriction_enabled ? "enabled": "DISABLED",
+                TALER_age_mask_to_string (&conf->mask));
+
   }
 }
 
@@ -143,14 +162,30 @@ extension_update_event_cb (void *cls,
 enum GNUNET_GenericReturnValue
 TEH_extensions_init ()
 {
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_extension_age_restriction_register ());
-
   /* Set the event handler for updates */
   struct GNUNET_DB_EventHeaderP ev = {
     .size = htons (sizeof (ev)),
     .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
   };
+
+  /* Load the shared libraries first */
+  if (GNUNET_OK !=
+      TALER_extensions_init (TEH_cfg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "failed to load extensions");
+    return GNUNET_SYSERR;
+  }
+
+  /* Check for age restriction */
+  {
+    const struct TALER_AgeRestrictionConfig *arc;
+
+    if (NULL !=
+        (arc = TALER_extensions_get_age_restriction_config ()))
+      TEH_age_restriction_config = *arc;
+  }
+
   extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
                                             GNUNET_TIME_UNIT_FOREVER_REL,
                                             &ev,
@@ -162,17 +197,24 @@ TEH_extensions_init ()
     return GNUNET_SYSERR;
   }
 
-  /* FIXME #7270: shall we load the extensions from the config right away?
-   * We do have to for now, as otherwise denominations with age restriction
-   * will not have the age mask set right upon initial generation.
-   */
-  TALER_extensions_load_taler_config (TEH_cfg);
-
   /* Trigger the initial load of configuration from the db */
-  for (const struct TALER_Extension *it = TALER_extensions_get_head ();
-       NULL != it->next;
+  for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
+       NULL != it && NULL != it->extension;
        it = it->next)
-    extension_update_event_cb (NULL, &it->type, sizeof(it->type));
+  {
+    const struct TALER_Extension *ext = it->extension;
+    uint32_t typ = htonl (ext->type);
+    char *manifest = json_dumps (ext->manifest (ext), JSON_COMPACT);
+
+    TEH_plugin->set_extension_manifest (TEH_plugin->cls,
+                                        ext->name,
+                                        manifest);
+
+    extension_update_event_cb (NULL,
+                               &typ,
+                               sizeof(typ));
+    free (manifest);
+  }
 
   return GNUNET_OK;
 }
@@ -190,4 +232,168 @@ TEH_extensions_done ()
 }
 
 
+/*
+ * @brief Execute database transactions for /extensions/policy_* POST requests.
+ *
+ * @param cls a `struct TALER_PolicyFulfillmentOutcome`
+ * @param connection MHD request context
+ * @param[out] mhd_ret set to MHD status on error
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+policy_fulfillment_transaction (
+  void *cls,
+  struct MHD_Connection *connection,
+  MHD_RESULT *mhd_ret)
+{
+  struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls;
+
+  return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls,
+                                                   fulfillment);
+}
+
+
+MHD_RESULT
+TEH_extensions_post_handler (
+  struct TEH_RequestContext *rc,
+  const json_t *root,
+  const char *const args[])
+{
+  const struct TALER_Extension *ext = NULL;
+  json_t *output;
+  struct TALER_PolicyDetails *policy_details = NULL;
+  size_t policy_details_count = 0;
+
+
+  if (NULL == args[0])
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (rc->connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+                                       "/extensions/$EXTENSION");
+  }
+
+  ext = TALER_extensions_get_by_name (args[0]);
+  if (NULL == ext)
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (rc->connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+                                       "/extensions/$EXTENSION unknown");
+  }
+
+  if (NULL == ext->policy_post_handler)
+    return TALER_MHD_reply_with_error (rc->connection,
+                                       MHD_HTTP_NOT_IMPLEMENTED,
+                                       
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+                                       "POST /extensions/$EXTENSION not 
supported");
+
+  /*  Extract hash_codes and retrieve related policy_details from the DB */
+  {
+    enum GNUNET_GenericReturnValue ret;
+    enum GNUNET_DB_QueryStatus qs;
+    const char *error_msg;
+    struct GNUNET_HashCode *hcs;
+    size_t len;
+    json_t*val;
+    size_t idx;
+    json_t *jhash_codes = json_object_get (root,
+                                           "policy_hash_codes");
+    if (! json_is_array (jhash_codes))
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+                                         "policy_hash_codes are missing");
+
+    len = json_array_size (jhash_codes);
+    hcs = GNUNET_new_array (len,
+                            struct GNUNET_HashCode);
+    policy_details = GNUNET_new_array (len,
+                                       struct TALER_PolicyDetails);
+
+    json_array_foreach (jhash_codes, idx, val)
+    {
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]),
+        GNUNET_JSON_spec_end ()
+      };
+
+      ret = GNUNET_JSON_parse (val,
+                               spec,
+                               &error_msg,
+                               NULL);
+      if (GNUNET_OK != ret)
+        break;
+
+      qs = TEH_plugin->get_policy_details (TEH_plugin->cls,
+                                           &hcs[idx],
+                                           &policy_details[idx]);
+      if (qs < 0)
+      {
+        error_msg = "a policy_hash_code couldn't be found";
+        break;
+      }
+    }
+
+    GNUNET_free (hcs);
+    if (GNUNET_OK != ret)
+    {
+      GNUNET_free (policy_details);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+                                         error_msg);
+    }
+  }
+
+
+  {
+    enum GNUNET_GenericReturnValue ret;
+
+    ret = ext->policy_post_handler (root,
+                                    &args[1],
+                                    policy_details,
+                                    policy_details_count,
+                                    &output);
+
+    if (GNUNET_OK != ret)
+    {
+      TALER_MHD_reply_json_steal (
+        rc->connection,
+        output,
+        MHD_HTTP_BAD_REQUEST);
+    }
+
+    /* execute fulfillment transaction */
+    {
+      MHD_RESULT mhd_ret;
+      struct TALER_PolicyFulfillmentTransactionData fulfillment = {
+        .proof = root,
+        .timestamp = GNUNET_TIME_timestamp_get (),
+        .details = policy_details,
+        .details_count = policy_details_count
+      };
+
+      if (GNUNET_OK !=
+          TEH_DB_run_transaction (rc->connection,
+                                  "execute policy fulfillment",
+                                  TEH_MT_REQUEST_POLICY_FULFILLMENT,
+                                  &mhd_ret,
+                                  &policy_fulfillment_transaction,
+                                  &fulfillment))
+      {
+        json_decref (output);
+        return mhd_ret;
+      }
+    }
+  }
+
+  return TALER_MHD_reply_json_steal (rc->connection,
+                                     output,
+                                     MHD_HTTP_OK);
+}
+
+
 /* end of taler-exchange-httpd_extensions.c */
diff --git a/src/exchange/taler-exchange-httpd_extensions.h 
b/src/exchange/taler-exchange-httpd_extensions.h
index 4659b653..e435f8f0 100644
--- a/src/exchange/taler-exchange-httpd_extensions.h
+++ b/src/exchange/taler-exchange-httpd_extensions.h
@@ -40,4 +40,19 @@ TEH_extensions_init (void);
 void
 TEH_extensions_done (void);
 
+
+/**
+ * Handle POST "/extensions/..." requests.
+ *
+ * @param rc request context
+ * @param root uploaded JSON data
+ * @param args array of additional options
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_extensions_post_handler (
+  struct TEH_RequestContext *rc,
+  const json_t *root,
+  const char *const args[]);
+
 #endif
diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 9b7f28bd..30c33653 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -17,6 +17,7 @@
  * @file taler-exchange-httpd_keys.c
  * @brief management of our various keys
  * @author Christian Grothoff
+ * @author Özgür Kesim
  */
 #include "platform.h"
 #include "taler_json_lib.h"
@@ -815,10 +816,7 @@ static struct TALER_AgeMask
 load_age_mask (const char*section_name)
 {
   static const struct TALER_AgeMask null_mask = {0};
-  struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask ();
-
-  if (age_mask.bits == 0)
-    return null_mask;
+  enum GNUNET_GenericReturnValue ret;
 
   if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
                       TEH_cfg,
@@ -826,22 +824,29 @@ load_age_mask (const char*section_name)
                       "AGE_RESTRICTED")))
     return null_mask;
 
+  if (GNUNET_SYSERR ==
+      (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
+                                                   section_name,
+                                                   "AGE_RESTRICTED")))
   {
-    enum GNUNET_GenericReturnValue ret;
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               section_name,
+                               "AGE_RESTRICTED",
+                               "Value must be YES or NO\n");
+    return null_mask;
+  }
 
-    if (GNUNET_SYSERR ==
-        (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
-                                                     section_name,
-                                                     "AGE_RESTRICTED")))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 section_name,
-                                 "AGE_RESTRICTED",
-                                 "Value must be YES or NO\n");
-      return null_mask;
-    }
+  if (GNUNET_OK == ret)
+  {
+    if (! TEH_age_restriction_enabled)
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "age restriction set in section %s, yet, age restriction is 
not enabled\n",
+                  section_name);
+    return TEH_age_restriction_config.mask;
   }
-  return age_mask;
+
+
+  return null_mask;
 }
 
 
@@ -1898,41 +1903,29 @@ create_krd (struct TEH_KeyStateHandle *ksh,
     bool has_extensions = false;
 
     /* Fill in the configurations of the enabled extensions */
-    for (const struct TALER_Extension *extension = TALER_extensions_get_head 
();
-         NULL != extension;
-         extension = extension->next)
+    for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
+         NULL != iter && NULL != iter->extension;
+         iter = iter->next)
     {
-      json_t *ext;
-      json_t *config_json;
+      const struct TALER_Extension *extension = iter->extension;
+      json_t *manifest;
       int r;
 
-      /* skip if not configured == disabled */
-      if (NULL == extension->config ||
-          NULL == extension->config_json)
+      /* skip if not enabled */
+      if (! extension->enabled)
         continue;
 
       /* flag our findings so far */
       has_extensions = true;
 
-      GNUNET_assert (NULL != extension->config_json);
 
-      config_json = json_copy (extension->config_json);
-      GNUNET_assert (NULL != config_json);
-
-      ext = GNUNET_JSON_PACK (
-        GNUNET_JSON_pack_bool ("critical",
-                               extension->critical),
-        GNUNET_JSON_pack_string ("version",
-                                 extension->version),
-        GNUNET_JSON_pack_object_steal ("config",
-                                       config_json)
-        );
-      GNUNET_assert (NULL != ext);
+      manifest = extension->manifest (extension);
+      GNUNET_assert (manifest);
 
       r = json_object_set_new (
         extensions,
         extension->name,
-        ext);
+        manifest);
       GNUNET_assert (0 == r);
     }
 
@@ -1948,12 +1941,16 @@ create_krd (struct TEH_KeyStateHandle *ksh,
         extensions);
       GNUNET_assert (0 == r);
 
-      sig = GNUNET_JSON_PACK (
-        GNUNET_JSON_pack_data_auto ("extensions_sig",
-                                    &TEH_extensions_sig));
+      /* Add the signature of the extensions, if it is not zero */
+      if (TEH_extensions_signed)
+      {
+        sig = GNUNET_JSON_PACK (
+          GNUNET_JSON_pack_data_auto ("extensions_sig",
+                                      &TEH_extensions_sig));
 
-      r = json_object_update (keys, sig);
-      GNUNET_assert (0 == r);
+        r = json_object_update (keys, sig);
+        GNUNET_assert (0 == r);
+      }
     }
     else
     {
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c 
b/src/exchange/taler-exchange-httpd_management_extensions.c
index a663b1b0..989b88fb 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -38,7 +38,7 @@
 struct Extension
 {
   enum TALER_Extension_Type type;
-  json_t *config;
+  json_t *manifest;
 };
 
 /**
@@ -52,7 +52,7 @@ struct SetExtensionsContext
 };
 
 /**
- * Function implementing database transaction to set the configuration of
+ * Function implementing database transaction to set the manifests of
  * extensions.  It runs the transaction logic.
  *  - IF it returns a non-error code, the transaction logic MUST NOT queue a
  *    MHD response.
@@ -74,13 +74,13 @@ set_extensions (void *cls,
 {
   struct SetExtensionsContext *sec = cls;
 
-  /* save the configurations of all extensions */
+  /* save the manifests of all extensions */
   for (uint32_t i = 0; i<sec->num_extensions; i++)
   {
     struct Extension *ext = &sec->extensions[i];
     const struct TALER_Extension *taler_ext;
     enum GNUNET_DB_QueryStatus qs;
-    char *config;
+    char *manifest;
 
     taler_ext = TALER_extensions_get_by_type (ext->type);
     if (NULL == taler_ext)
@@ -90,10 +90,8 @@ set_extensions (void *cls,
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
 
-    GNUNET_assert (NULL != ext->config);
-
-    config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS);
-    if (NULL == config)
+    manifest = json_dumps (ext->manifest, JSON_COMPACT | JSON_SORT_KEYS);
+    if (NULL == manifest)
     {
       GNUNET_break (0);
       *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -103,10 +101,12 @@ set_extensions (void *cls,
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
 
-    qs = TEH_plugin->set_extension_config (
+    qs = TEH_plugin->set_extension_manifest (
       TEH_plugin->cls,
       taler_ext->name,
-      config);
+      manifest);
+
+    free (manifest);
 
     if (qs < 0)
     {
@@ -137,6 +137,7 @@ set_extensions (void *cls,
 
   /* All extensions configured, update the signature */
   TEH_extensions_sig = sec->extensions_sig;
+  TEH_extensions_signed = true;
 
   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, 
matters here */
 }
@@ -150,7 +151,7 @@ verify_extensions_from_json (
   const char*name;
   const struct TALER_Extension *extension;
   size_t i = 0;
-  json_t *blob;
+  json_t *manifest;
 
   GNUNET_assert (NULL != extensions);
   GNUNET_assert (json_is_object (extensions));
@@ -159,7 +160,7 @@ verify_extensions_from_json (
   sec->extensions = GNUNET_new_array (sec->num_extensions,
                                       struct Extension);
 
-  json_object_foreach (extensions, name, blob)
+  json_object_foreach (extensions, name, manifest)
   {
     int critical = 0;
     json_t *config;
@@ -175,18 +176,18 @@ verify_extensions_from_json (
     }
 
     if (GNUNET_OK !=
-        TALER_extensions_is_json_config (
-          blob, &critical, &version, &config))
+        TALER_extensions_parse_manifest (
+          manifest, &critical, &version, &config))
       return GNUNET_SYSERR;
 
     if (critical != extension->critical
         || 0 != strcmp (version, extension->version) // FIXME-oec: libtool 
compare
         || NULL == config
-        || GNUNET_OK != extension->test_json_config (config))
+        || GNUNET_OK != extension->load_config (NULL, config))
       return GNUNET_SYSERR;
 
     sec->extensions[i].type = extension->type;
-    sec->extensions[i].config = config;
+    sec->extensions[i].manifest = json_copy (manifest);
   }
 
   return GNUNET_OK;
@@ -223,7 +224,8 @@ TEH_handler_management_post_extensions (
   }
 
   /* Ensure we have an object */
-  if (! json_is_object (extensions))
+  if ((! json_is_object (extensions)) &&
+      (! json_is_null (extensions)))
   {
     GNUNET_JSON_parse_free (top_spec);
     return TALER_MHD_reply_with_error (
@@ -235,13 +237,13 @@ TEH_handler_management_post_extensions (
 
   /* Verify the signature */
   {
-    struct TALER_ExtensionConfigHashP h_config;
+    struct TALER_ExtensionManifestsHashP h_manifests;
 
     if (GNUNET_OK !=
-        TALER_JSON_extensions_config_hash (extensions, &h_config) ||
+        TALER_JSON_extensions_manifests_hash (extensions, &h_manifests) ||
         GNUNET_OK !=
-        TALER_exchange_offline_extension_config_hash_verify (
-          &h_config,
+        TALER_exchange_offline_extension_manifests_hash_verify (
+          &h_manifests,
           &TEH_master_public_key,
           &sec.extensions_sig))
     {
@@ -298,9 +300,9 @@ TEH_handler_management_post_extensions (
 CLEANUP:
   for (unsigned int i = 0; i < sec.num_extensions; i++)
   {
-    if (NULL != sec.extensions[i].config)
+    if (NULL != sec.extensions[i].manifest)
     {
-      json_decref (sec.extensions[i].config);
+      json_decref (sec.extensions[i].manifest);
     }
   }
   GNUNET_free (sec.extensions);
diff --git a/src/exchange/taler-exchange-httpd_metrics.h 
b/src/exchange/taler-exchange-httpd_metrics.h
index c1b7326f..cae371f7 100644
--- a/src/exchange/taler-exchange-httpd_metrics.h
+++ b/src/exchange/taler-exchange-httpd_metrics.h
@@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest
   TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
   TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
   TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
-  TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */
+  TEH_MT_REQUEST_POLICY_FULFILLMENT = 13,
+  TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
 };
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index a25d6ff4..85090ced 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations (
     bool failed = true;
 
     /* Has been checked in handle_refreshes_reveal_json() */
-    GNUNET_assert (ng ==
-                   TALER_extensions_age_restriction_num_groups ());
+    GNUNET_assert (ng == TEH_age_restriction_config.num_groups);
 
     rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
     oac = rctx->old_age_commitment;
@@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection 
*connection,
   /* Sanity check of age commitment: If it was provided, it _must_ be an array
    * of the size the # of age groups */
   if (NULL != old_age_commitment_json
-      && TALER_extensions_age_restriction_num_groups () !=
+      && TEH_age_restriction_config.num_groups !=
       json_array_size (old_age_commitment_json))
   {
     GNUNET_break_op (0);
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index a81d1b6e..ca110ad4 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -76,7 +76,7 @@ TEH_RESPONSE_compile_transaction_history (
               &h_wire,
               &deposit->h_contract_terms,
               &deposit->h_age_commitment,
-              NULL /* h_extensions! */,
+              &deposit->h_policy,
               &deposit->h_denom_pub,
               deposit->timestamp,
               &deposit->merchant_pub,
diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql
index ab4f8ea9..a95d74d2 100644
--- a/src/exchangedb/common-0001.sql
+++ b/src/exchangedb/common-0001.sql
@@ -907,8 +907,8 @@ BEGIN
       ',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)'
       ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
       ',done BOOLEAN NOT NULL DEFAULT FALSE'
-      ',extension_blocked BOOLEAN NOT NULL DEFAULT FALSE'
-      ',extension_details_serial_id INT8' -- REFERENCES extension_details 
(extension_details_serial_id) ON DELETE CASCADE'
+      ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE'
+      ',policy_details_serial_id INT8' -- REFERENCES policy_details 
(policy_details_serial_id) ON DELETE CASCADE'
     ') %s ;'
     ,table_name
     ,'PARTITION BY HASH (coin_pub)'
@@ -2619,7 +2619,7 @@ BEGIN
 
   ALTER TABLE IF EXISTS deposits
     DROP CONSTRAINT IF EXISTS deposits_pkey CASCADE
-    ,DROP CONSTRAINT IF EXISTS deposits_extension_details_serial_id_fkey
+    ,DROP CONSTRAINT IF EXISTS deposits_policy_details_serial_id_fkey
     ,DROP CONSTRAINT IF EXISTS 
deposits_coin_pub_merchant_pub_h_contract_terms_key CASCADE
   ;
 
diff --git a/src/exchangedb/exchange-0001-part.sql 
b/src/exchangedb/exchange-0001-part.sql
index 99883a27..4599d2ee 100644
--- a/src/exchangedb/exchange-0001-part.sql
+++ b/src/exchangedb/exchange-0001-part.sql
@@ -411,19 +411,19 @@ COMMENT ON TABLE signkey_revocations
   IS 'Table storing which online signing keys have been revoked';
 
 
--- ------------------------------ extension 
----------------------------------------
+-- ------------------------------ extensions 
----------------------------------------
 
 CREATE TABLE IF NOT EXISTS extensions
   (extension_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
   ,name VARCHAR NOT NULL UNIQUE
-  ,config BYTEA
+  ,manifest BYTEA
   );
 COMMENT ON TABLE extensions
   IS 'Configurations of the activated extensions';
 COMMENT ON COLUMN extensions.name
   IS 'Name of the extension';
-COMMENT ON COLUMN extensions.config
-  IS 'Configuration of the extension as JSON-blob, maybe NULL';
+COMMENT ON COLUMN extensions.manifest
+  IS 'Manifest of the extension as JSON-blob, maybe NULL.  It contains common 
meta-information and extension-specific configuration.';
 
 
 -- ------------------------------ known_coins 
----------------------------------------
@@ -520,21 +520,69 @@ CREATE TABLE IF NOT EXISTS refresh_transfer_keys_default
 SELECT add_constraints_to_refresh_transfer_keys_partition('default');
 
 
--- ------------------------------ extension_details 
----------------------------------------
-
-CREATE TABLE IF NOT EXISTS extension_details
-  (extension_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY 
KEY
-  ,extension_options VARCHAR)
-  PARTITION BY HASH (extension_details_serial_id);
-COMMENT ON TABLE extension_details
-  IS 'Extensions that were provided with deposits (not yet used).';
-COMMENT ON COLUMN extension_details.extension_options
-  IS 'JSON object with options set that the exchange needs to consider when 
executing a deposit. Supported details depend on the extensions supported by 
the exchange.';
-
-CREATE TABLE IF NOT EXISTS extension_details_default
-  PARTITION OF extension_details
-  FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+-- ------------------------------ policy_fulfillments 
-------------------------------------
 
+CREATE TABLE IF NOT EXISTS policy_fulfillments
+  (fulfillment_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY
+  ,fulfillment_timestamp INT8 NOT NULL
+  ,fulfillment_proof VARCHAR
+  ,h_fulfillment_proof BYTEA NOT NULL CHECK(LENGTH(h_fulfillment_proof) = 64) 
UNIQUE
+  ,policy_hash_codes BYTEA NOT NULL CHECK(0 = MOD(LENGTH(policy_hash_codes), 
16))
+  );
+COMMENT ON TABLE policy_fulfillments
+  IS 'Proofs of fulfillment of policies that were set in deposits';
+COMMENT ON COLUMN policy_fulfillments.fulfillment_timestamp
+  IS 'Timestamp of the arrival of a proof of fulfillment';
+COMMENT ON COLUMN policy_fulfillments.fulfillment_proof
+  IS 'JSON object with a proof of the fulfillment of a policy. Supported 
details depend on the policy extensions supported by the exchange.';
+COMMENT ON COLUMN policy_fulfillments.h_fulfillment_proof
+  IS 'Hash of the fulfillment_proof';
+COMMENT ON COLUMN policy_fulfillments.policy_hash_codes
+  IS 'Concatenation of the policy_hash_code of all policy_details that are 
fulfilled by this proof';
+
+-- ------------------------------ policy_details 
----------------------------------------
+
+CREATE TABLE IF NOT EXISTS policy_details
+  (policy_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+  ,policy_hash_code BYTEA PRIMARY KEY CHECK(LENGTH(policy_hash_code)=16)
+  ,policy_json VARCHAR
+  ,deadline INT8 NOT NULL
+  ,commitment_val INT8 NOT NULL
+  ,commitment_frac INT4 NOT NULL
+  ,accumulated_total_val INT8 NOT NULL
+  ,accumulated_total_frac INT4 NOT NULL
+  ,fee_val INT8 NOT NULL
+  ,fee_frac INT4 NOT NULL
+  ,transferable_val INT8 NOT NULL
+  ,transferable_frac INT8 NOT NULL
+  ,fulfillment_state smallint NOT NULL CHECK(fulfillment_state between 0 and 5)
+  ,fulfillment_id BIGINT NULL REFERENCES policy_fulfillments (fulfillment_id) 
ON DELETE CASCADE
+  );
+COMMENT ON TABLE policy_details
+  IS 'Policies that were provided with deposits via policy extensions.';
+COMMENT ON COLUMN policy_details.policy_hash_code
+  IS 'ID (GNUNET_HashCode) that identifies a policy.  Will be calculated by 
the policy extension based on the content';
+COMMENT ON COLUMN policy_details.policy_json
+  IS 'JSON object with options set that the exchange needs to consider when 
executing a deposit. Supported details depend on the policy extensions 
supported by the exchange.';
+COMMENT ON COLUMN policy_details.deadline
+  IS 'Deadline until the policy must be marked as fulfilled (maybe "forever")';
+COMMENT ON COLUMN policy_details.commitment_val
+  IS 'The amount that this policy commits to.  Invariant: commitment >= fee';
+COMMENT ON COLUMN policy_details.accumulated_total_val
+  IS 'The sum of all contributions of all deposit that reference this policy.  
Invariant: The fulfilment_state must be Insufficient as long as 
accumulated_total < commitment';
+COMMENT ON COLUMN policy_details.fee_val
+  IS 'The fee for this policy, due when the policy is fulfilled or timed out';
+COMMENT ON COLUMN policy_details.transferable_val
+  IS 'The amount that on fulfilment or timeout will be transfered to the 
payto-URI''s of the corresponding deposit''s.  The policy fees must have been 
already deducted from it.  Invariant: fee+transferable <= accumulated_total.  
The remaining amount (accumulated_total - fee - transferable) can be refreshed 
by the owner of the coins when the state is Timeout or Success.';
+COMMENT ON COLUMN policy_details.fulfillment_state
+  IS 'State of the fulfillment:
+       - 0 (Failure)
+       - 1 (Insufficient)
+       - 2 (Ready)
+       - 4 (Success)
+       - 5 (Timeout)';
+COMMENT ON COLUMN policy_details.fulfillment_id
+  IS 'Reference to the proof of the fulfillment of this policy, if it exists.  
Invariant: If not NULL, this entry''s .hash_code MUST be part of the 
corresponding policy_fulfillments.policy_hash_codes array.';
 
 -- ------------------------------ deposits 
----------------------------------------
 
@@ -552,10 +600,10 @@ COMMENT ON COLUMN deposits.wire_salt
   IS 'Salt used when hashing the payto://-URI to get the h_wire';
 COMMENT ON COLUMN deposits.done
   IS 'Set to TRUE once we have included this deposit in some aggregate wire 
transfer to the merchant';
-COMMENT ON COLUMN deposits.extension_blocked
-  IS 'True if the aggregation of the deposit is currently blocked by some 
extension mechanism. Used to filter out deposits that must not be processed by 
the canonical deposit logic.';
-COMMENT ON COLUMN deposits.extension_details_serial_id
-  IS 'References extensions table, NULL if extensions are not used';
+COMMENT ON COLUMN deposits.policy_blocked
+  IS 'True if the aggregation of the deposit is currently blocked by some 
policy extension mechanism. Used to filter out deposits that must not be 
processed by the canonical deposit logic.';
+COMMENT ON COLUMN deposits.policy_details_serial_id
+  IS 'References policy extensions table, NULL if extensions are not used';
 
 CREATE TABLE IF NOT EXISTS deposits_default
   PARTITION OF deposits
@@ -591,7 +639,7 @@ CREATE OR REPLACE FUNCTION deposits_insert_trigger()
 DECLARE
   is_ready BOOLEAN;
 BEGIN
-  is_ready  = NOT (NEW.done OR NEW.extension_blocked);
+  is_ready  = NOT (NEW.done OR NEW.policy_blocked);
 
   IF (is_ready)
   THEN
@@ -635,8 +683,8 @@ DECLARE
 DECLARE
   is_ready BOOLEAN;
 BEGIN
-  was_ready = NOT (OLD.done OR OLD.extension_blocked);
-  is_ready  = NOT (NEW.done OR NEW.extension_blocked);
+  was_ready = NOT (OLD.done OR OLD.policy_blocked);
+  is_ready  = NOT (NEW.done OR NEW.policy_blocked);
   IF (was_ready AND NOT is_ready)
   THEN
     DELETE FROM exchange.deposits_by_ready
@@ -690,7 +738,7 @@ CREATE OR REPLACE FUNCTION deposits_delete_trigger()
 DECLARE
   was_ready BOOLEAN;
 BEGIN
-  was_ready  = NOT (OLD.done OR OLD.extension_blocked);
+  was_ready  = NOT (OLD.done OR OLD.policy_blocked);
 
   IF (was_ready)
   THEN
diff --git a/src/exchangedb/pg_insert_records_by_table.c 
b/src/exchangedb/pg_insert_records_by_table.c
index 0ac70bae..d6630797 100644
--- a/src/exchangedb/pg_insert_records_by_table.c
+++ b/src/exchangedb/pg_insert_records_by_table.c
@@ -872,11 +872,11 @@ irbt_cb_table_deposits (struct PostgresClosure *pg,
     GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.wire_salt),
     GNUNET_PQ_query_param_auto_from_type (
       &td->details.deposits.wire_target_h_payto),
-    GNUNET_PQ_query_param_bool (td->details.deposits.extension_blocked),
-    0 == td->details.deposits.extension_details_serial_id
+    GNUNET_PQ_query_param_bool (td->details.deposits.policy_blocked),
+    0 == td->details.deposits.policy_details_serial_id
     ? GNUNET_PQ_query_param_null ()
     : GNUNET_PQ_query_param_uint64 (
-      &td->details.deposits.extension_details_serial_id),
+      &td->details.deposits.policy_details_serial_id),
     GNUNET_PQ_query_param_end
   };
 
@@ -898,8 +898,8 @@ irbt_cb_table_deposits (struct PostgresClosure *pg,
            ",coin_sig"
            ",wire_salt"
            ",wire_target_h_payto"
-           ",extension_blocked"
-           ",extension_details_serial_id"
+           ",policy_blocked"
+           ",policy_details_serial_id"
            ") VALUES "
            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
            " $11, $12, $13, $14, $15, $16, $17);");
@@ -1217,9 +1217,9 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&td->serial),
     GNUNET_PQ_query_param_string (td->details.extensions.name),
-    NULL == td->details.extensions.config ?
+    NULL == td->details.extensions.manifest ?
     GNUNET_PQ_query_param_null () :
-    GNUNET_PQ_query_param_string (td->details.extensions.config),
+    GNUNET_PQ_query_param_string (td->details.extensions.manifest),
     GNUNET_PQ_query_param_end
   };
 
@@ -1228,7 +1228,7 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
            "INSERT INTO extensions"
            "(extension_id"
            ",name"
-           ",config"
+           ",manifest"
            ") VALUES "
            "($1, $2, $3);");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
@@ -1238,34 +1238,99 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
 
 
 /**
- * Function called with extension_details records to insert into table.
+ * Function called with policy_details records to insert into table.
  *
  * @param pg plugin context
  * @param td record to insert
  */
 static enum GNUNET_DB_QueryStatus
-irbt_cb_table_extension_details (struct PostgresClosure *pg,
-                                 const struct TALER_EXCHANGEDB_TableData *td)
+irbt_cb_table_policy_details (struct PostgresClosure *pg,
+                              const struct TALER_EXCHANGEDB_TableData *td)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&td->serial),
-    NULL ==
-    td->details.extension_details.extension_options ?
-    GNUNET_PQ_query_param_null () :
-    GNUNET_PQ_query_param_string (
-      td->details.extension_details.extension_options),
+    GNUNET_PQ_query_param_auto_from_type (
+      &td->details.policy_details.hash_code),
+    (td->details.policy_details.no_policy_json)
+      ? GNUNET_PQ_query_param_null ()
+      : TALER_PQ_query_param_json (td->details.policy_details.policy_json),
+    TALER_PQ_query_param_amount (&td->details.policy_details.commitment),
+    TALER_PQ_query_param_amount 
(&td->details.policy_details.accumulated_total),
+    TALER_PQ_query_param_amount (&td->details.policy_details.fee),
+    TALER_PQ_query_param_amount (&td->details.policy_details.transferable),
+    GNUNET_PQ_query_param_timestamp (&td->details.policy_details.deadline),
+    GNUNET_PQ_query_param_uint16 (
+      &td->details.policy_details.fulfillment_state),
+    (td->details.policy_details.no_fulfillment_id)
+      ? GNUNET_PQ_query_param_null ()
+      : GNUNET_PQ_query_param_uint64 (
+      &td->details.policy_details.fulfillment_id),
     GNUNET_PQ_query_param_end
   };
 
   PREPARE (pg,
-           "insert_into_table_extension_details",
-           "INSERT INTO extension_details"
-           "(extension_details_serial_id"
-           ",extension_options"
+           "insert_into_table_policy_details",
+           "INSERT INTO policy_details"
+           "(policy_details_serial_id"
+           ",policy_hash_code"
+           ",policy_json"
+           ",deadline"
+           ",commitment_val"
+           ",commitment_frac"
+           ",accumulated_total_val"
+           ",accumulated_total_frac"
+           ",fee_val"
+           ",fee_frac"
+           ",transferable_val"
+           ",transferable_frac"
+           ",fulfillment_state"
+           ",fulfillment_id"
            ") VALUES "
            "($1, $2);");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             
"insert_into_table_extension_details",
+                                             
"insert_into_table_policy_details",
+                                             params);
+}
+
+
+/**
+ * Function called with policy_fulfillment records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_policy_fulfillments (struct PostgresClosure *pg,
+                                   const struct TALER_EXCHANGEDB_TableData *td)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&td->serial),
+    GNUNET_PQ_query_param_timestamp (
+      &td->details.policy_fulfillments.fulfillment_timestamp),
+    (NULL == td->details.policy_fulfillments.fulfillment_proof)
+      ? GNUNET_PQ_query_param_null ()
+      : GNUNET_PQ_query_param_string (
+      td->details.policy_fulfillments.fulfillment_proof),
+    GNUNET_PQ_query_param_auto_from_type (
+      &td->details.policy_fulfillments.h_fulfillment_proof),
+    GNUNET_PQ_query_param_fixed_size (
+      td->details.policy_fulfillments.policy_hash_codes,
+      td->details.policy_fulfillments.policy_hash_codes_count),
+    GNUNET_PQ_query_param_end
+  };
+
+  PREPARE (pg,
+           "insert_into_table_policy_fulfillments",
+           "INSERT INTO policy_fulfillments "
+           "(fulfillment_id"
+           ",fulfillment_timestamp"
+           ",fulfillment_proof"
+           ",h_fulfillment_proof"
+           ",policy_hash_codes"
+           ") VALUES "
+           "($1, $2, $3, $4, $5);");
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             
"insert_into_table_policy_fulfillments",
                                              params);
 }
 
@@ -1900,8 +1965,11 @@ TEH_PG_insert_records_by_table (void *cls,
   case TALER_EXCHANGEDB_RT_EXTENSIONS:
     rh = &irbt_cb_table_extensions;
     break;
-  case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
-    rh = &irbt_cb_table_extension_details;
+  case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+    rh = &irbt_cb_table_policy_details;
+    break;
+  case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+    rh = &irbt_cb_table_policy_fulfillments;
     break;
   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
     rh = &irbt_cb_table_purse_requests;
diff --git a/src/exchangedb/pg_lookup_records_by_table.c 
b/src/exchangedb/pg_lookup_records_by_table.c
index 57c7c2b0..9d600f74 100644
--- a/src/exchangedb/pg_lookup_records_by_table.c
+++ b/src/exchangedb/pg_lookup_records_by_table.c
@@ -927,7 +927,7 @@ lrbt_cb_table_deposits (void *cls,
 
   for (unsigned int i = 0; i<num_results; i++)
   {
-    bool no_extension;
+    bool no_policy;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 (
         "serial",
@@ -972,13 +972,13 @@ lrbt_cb_table_deposits (void *cls,
         "wire_target_h_payto",
         &td.details.deposits.wire_target_h_payto),
       GNUNET_PQ_result_spec_auto_from_type (
-        "extension_blocked",
-        &td.details.deposits.extension_blocked),
+        "policy_blocked",
+        &td.details.deposits.policy_blocked),
       GNUNET_PQ_result_spec_allow_null (
         GNUNET_PQ_result_spec_uint64 (
-          "extension_details_serial_id",
-          &td.details.deposits.extension_details_serial_id),
-        &no_extension),
+          "policy_details_serial_id",
+          &td.details.deposits.policy_details_serial_id),
+        &no_policy),
       GNUNET_PQ_result_spec_end
     };
 
@@ -1414,7 +1414,7 @@ lrbt_cb_table_extensions (void *cls,
   struct TALER_EXCHANGEDB_TableData td = {
     .table = TALER_EXCHANGEDB_RT_EXTENSIONS
   };
-  bool no_config = false;
+  bool no_manifest = false;
 
   for (unsigned int i = 0; i<num_results; i++)
   {
@@ -1424,9 +1424,9 @@ lrbt_cb_table_extensions (void *cls,
       GNUNET_PQ_result_spec_string ("name",
                                     &td.details.extensions.name),
       GNUNET_PQ_result_spec_allow_null (
-        GNUNET_PQ_result_spec_string ("config",
-                                      &td.details.extensions.config),
-        &no_config),
+        GNUNET_PQ_result_spec_string ("manifest",
+                                      &td.details.extensions.manifest),
+        &no_manifest),
       GNUNET_PQ_result_spec_end
     };
 
@@ -1447,33 +1447,112 @@ lrbt_cb_table_extensions (void *cls,
 
 
 /**
- * Function called with extension_details table entries.
+ * Function called with policy_details table entries.
  *
  * @param cls closure
  * @param result the postgres result
  * @param num_results the number of results in @a result
  */
 static void
-lrbt_cb_table_extension_details (void *cls,
-                                 PGresult *result,
-                                 unsigned int num_results)
+lrbt_cb_table_policy_details (void *cls,
+                              PGresult *result,
+                              unsigned int num_results)
+{
+  struct LookupRecordsByTableContext *ctx = cls;
+  struct PostgresClosure *pg = ctx->pg;
+  struct TALER_EXCHANGEDB_TableData td = {
+    .table = TALER_EXCHANGEDB_RT_POLICY_DETAILS
+  };
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
+                                    &td.serial),
+      GNUNET_PQ_result_spec_auto_from_type ("hash_code",
+                                            &td.details.policy_details.
+                                            hash_code),
+      GNUNET_PQ_result_spec_allow_null (
+        TALER_PQ_result_spec_json ("policy_json",
+                                   &td.details.policy_details.
+                                   policy_json),
+        &td.details.policy_details.no_policy_json),
+      GNUNET_PQ_result_spec_timestamp ("deadline",
+                                       &td.details.policy_details.
+                                       deadline),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
+                                   &td.details.policy_details.
+                                   commitment),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+                                   &td.details.policy_details.
+                                   accumulated_total),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("fee",
+                                   &td.details.policy_details.
+                                   fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("transferable",
+                                   &td.details.policy_details.
+                                   transferable),
+      GNUNET_PQ_result_spec_uint16 ("fulfillment_state",
+                                    &td.details.policy_details.
+                                    fulfillment_state),
+      GNUNET_PQ_result_spec_allow_null (
+        GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
+                                      &td.details.policy_details.
+                                      fulfillment_id),
+        &td.details.policy_details.no_fulfillment_id),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      ctx->error = true;
+      return;
+    }
+    ctx->cb (ctx->cb_cls,
+             &td);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+/**
+ * Function called with policy_fulfillments table entries.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lrbt_cb_table_policy_fulfillments (void *cls,
+                                   PGresult *result,
+                                   unsigned int num_results)
 {
   struct LookupRecordsByTableContext *ctx = cls;
   struct TALER_EXCHANGEDB_TableData td = {
-    .table = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS
+    .table = TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS
   };
 
   for (unsigned int i = 0; i<num_results; i++)
   {
-    bool no_config = false;
+    bool no_proof = false;
+    bool no_timestamp = false;
     struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_uint64 ("extension_details_serial_id",
+      GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
                                     &td.serial),
       GNUNET_PQ_result_spec_allow_null (
-        GNUNET_PQ_result_spec_string ("extension_options",
-                                      &td.details.extension_details.
-                                      extension_options),
-        &no_config),
+        GNUNET_PQ_result_spec_timestamp ("fulfillment_timestamp",
+                                         &td.details.policy_fulfillments.
+                                         fulfillment_timestamp),
+        &no_timestamp),
+      GNUNET_PQ_result_spec_allow_null (
+        GNUNET_PQ_result_spec_string ("fulfillment_proof",
+                                      &td.details.policy_fulfillments.
+                                      fulfillment_proof),
+        &no_proof),
       GNUNET_PQ_result_spec_end
     };
 
@@ -2607,9 +2686,13 @@ TEH_PG_lookup_records_by_table (void *cls,
     statement = "select_above_serial_by_table_extensions";
     rh = &lrbt_cb_table_extensions;
     break;
-  case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
-    statement = "select_above_serial_by_table_extension_details";
-    rh = &lrbt_cb_table_extension_details;
+  case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+    statement = "select_above_serial_by_table_policy_details";
+    rh = &lrbt_cb_table_policy_details;
+    break;
+  case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+    statement = "select_above_serial_by_table_policy_fulfillments";
+    rh = &lrbt_cb_table_policy_fulfillments;
     break;
   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
     XPREPARE ("select_above_serial_by_table_purse_requests",
diff --git a/src/exchangedb/pg_lookup_serial_by_table.c 
b/src/exchangedb/pg_lookup_serial_by_table.c
index 202be30f..7e150cd2 100644
--- a/src/exchangedb/pg_lookup_serial_by_table.c
+++ b/src/exchangedb/pg_lookup_serial_by_table.c
@@ -277,12 +277,20 @@ TEH_PG_lookup_serial_by_table (void *cls,
               " ORDER BY extension_id DESC"
               " LIMIT 1;");
     break;
-  case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
-    XPREPARE ("select_serial_by_table_extension_details",
+  case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+    XPREPARE ("select_serial_by_table_policy_details",
               "SELECT"
-              " extension_details_serial_id AS serial"
-              " FROM extension_details"
-              " ORDER BY extension_details_serial_id DESC"
+              " policy_details_serial_id AS serial"
+              " FROM policy_details"
+              " ORDER BY policy_details_serial_id DESC"
+              " LIMIT 1;");
+    break;
+  case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+    XPREPARE ("select_serial_by_table_policy_fulfillments",
+              "SELECT"
+              " fulfillment_id AS serial"
+              " FROM policy_fulfillments"
+              " ORDER BY fulfillment_id DESC"
               " LIMIT 1;");
     break;
   case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 28dbdbaa..0b03fe4b 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2099,17 +2099,17 @@ prepare_statements (struct PostgresClosure *pg)
       " WHERE job_name=$1"
       "   AND start_row=$2"
       "   AND end_row=$3"),
-    /* Used in #postgres_set_extension_config */
+    /* Used in #postgres_set_extension_manifest */
     GNUNET_PQ_make_prepare (
-      "set_extension_config",
-      "INSERT INTO extensions (name, config) VALUES ($1, $2) "
+      "set_extension_manifest",
+      "INSERT INTO extensions (name, manifest) VALUES ($1, $2) "
       "ON CONFLICT (name) "
-      "DO UPDATE SET config=$2"),
-    /* Used in #postgres_get_extension_config */
+      "DO UPDATE SET manifest=$2"),
+    /* Used in #postgres_get_extension_manifest */
     GNUNET_PQ_make_prepare (
-      "get_extension_config",
+      "get_extension_manifest",
       "SELECT "
-      " config "
+      " manifest "
       "FROM extensions"
       "   WHERE name=$1;"),
     /* Used in #postgres_insert_contract() */
@@ -4083,7 +4083,7 @@ compute_shard (const struct TALER_MerchantPublicKeyP 
*merchant_pub)
  * @param deposit deposit operation details
  * @param known_coin_id row of the coin in the known_coins table
  * @param h_payto hash of the merchant's bank account details
- * @param extension_blocked true if an extension is blocking the wire transfer
+ * @param _blocked true if an extension is blocking the wire transfer
  * @param[in,out] exchange_timestamp time to use for the deposit (possibly 
updated)
  * @param[out] balance_ok set to true if the balance was sufficient
  * @param[out] in_conflict set to true if the deposit conflicted
@@ -4095,7 +4095,7 @@ postgres_do_deposit (
   const struct TALER_EXCHANGEDB_Deposit *deposit,
   uint64_t known_coin_id,
   const struct TALER_PaytoHashP *h_payto,
-  bool extension_blocked,
+  uint64_t *policy_details_serial_id,
   struct GNUNET_TIME_Timestamp *exchange_timestamp,
   bool *balance_ok,
   bool *in_conflict)
@@ -4117,10 +4117,10 @@ postgres_do_deposit (
     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
     GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
     GNUNET_PQ_query_param_uint64 (&deposit_shard),
-    GNUNET_PQ_query_param_bool (extension_blocked),
-    (NULL == deposit->extension_details)
+    GNUNET_PQ_query_param_bool (deposit->has_policy),
+    (NULL == policy_details_serial_id)
     ? GNUNET_PQ_query_param_null ()
-    : TALER_PQ_query_param_json (deposit->extension_details),
+    : GNUNET_PQ_query_param_uint64 (policy_details_serial_id),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
@@ -4140,6 +4140,101 @@ postgres_do_deposit (
 }
 
 
+/* Get the details of a policy, referenced by its hash code
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param hc The hash code under which the details to a particular policy 
should be found
+ * @param[out] details The found details
+ * @return query execution status
+ * */
+static enum GNUNET_DB_QueryStatus
+postgres_get_policy_details (
+  void *cls,
+  const struct GNUNET_HashCode *hc,
+  struct TALER_PolicyDetails *details)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (hc),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_timestamp ("deadline",
+                                     &details->deadline),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
+                                 &details->commitment),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+                                 &details->accumulated_total),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("policy_fee",
+                                 &details->policy_fee),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("transferable_amount",
+                                 &details->transferable_amount),
+    GNUNET_PQ_result_spec_auto_from_type ("state",
+                                          &details->fulfillment_state),
+    GNUNET_PQ_result_spec_allow_null (
+      GNUNET_PQ_result_spec_uint64 ("policy_fulfillment_id",
+                                    &details->policy_fulfillment_id),
+      &details->no_policy_fulfillment_id),
+    GNUNET_PQ_result_spec_end
+  };
+
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "get_policy_details",
+                                                   params,
+                                                   rs);
+}
+
+
+/* Persist the details to a policy in the policy_details table.  If there
+ * already exists a policy, update the fields accordingly.
+ *
+ * @param details The policy details that should be persisted.  If an entry for
+ *        the given details->hash_code exists, the values will be updated.
+ * @param[out] policy_details_serial_id The row ID of the policy details
+ * @param[out] accumulated_total The total amount accumulated in that policy
+ * @param[out] fulfillment_state The state of policy.  If the state was 
Insufficient prior to the call and the provided deposit raises the 
accumulated_total above the commitment, it will be set to Ready.
+ * @return query execution status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_persist_policy_details (
+  void *cls,
+  const struct TALER_PolicyDetails *details,
+  uint64_t *policy_details_serial_id,
+  struct TALER_Amount *accumulated_total,
+  enum TALER_PolicyFulfillmentState *fulfillment_state)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (&details->hash_code),
+    TALER_PQ_query_param_json (details->policy_json),
+    GNUNET_PQ_query_param_timestamp (&details->deadline),
+    TALER_PQ_query_param_amount (&details->commitment),
+    TALER_PQ_query_param_amount (&details->accumulated_total),
+    TALER_PQ_query_param_amount (&details->policy_fee),
+    TALER_PQ_query_param_amount (&details->transferable_amount),
+    GNUNET_PQ_query_param_auto_from_type (&details->fulfillment_state),
+    (details->no_policy_fulfillment_id)
+     ?  GNUNET_PQ_query_param_null ()
+     : GNUNET_PQ_query_param_uint64 (&details->policy_fulfillment_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
+                                  policy_details_serial_id),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+                                 accumulated_total),
+    GNUNET_PQ_result_spec_uint32 ("fulfillment_state",
+                                  fulfillment_state),
+    GNUNET_PQ_result_spec_end
+  };
+
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   
"call_insert_or_update_policy_details",
+                                                   params,
+                                                   rs);
+}
+
+
 /**
  * Perform melt operation, checking for sufficient balance
  * of the coin and possibly persisting the melt details.
@@ -4394,6 +4489,118 @@ postgres_do_recoup_refresh (
 }
 
 
+/*
+ * Compares two indices into an array of hash codes according to
+ * GNUNET_CRYPTO_hash_cmp of the content at those index positions.
+ *
+ * Used in a call qsort_t in order to generate sorted policy_hash_codes.
+ */
+static int
+hash_code_cmp (
+  const void *hc1,
+  const void *hc2,
+  void *arg)
+{
+  size_t i1 = *(size_t *) hc1;
+  size_t i2 = *(size_t *) hc2;
+  const struct TALER_PolicyDetails *d = arg;
+
+  return GNUNET_CRYPTO_hash_cmp (&d[i1].hash_code,
+                                 &d[i2].hash_code);
+}
+
+
+/**
+ * Add a proof of fulfillment into the policy_fulfillments table
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param[out] proof_id set record id for the proof
+ * @return query execution status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_add_policy_fulfillment_proof (
+  void *cls,
+  struct TALER_PolicyFulfillmentTransactionData *fulfillment)
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct PostgresClosure *pg = cls;
+  size_t count = fulfillment->details_count;
+  struct GNUNET_HashCode hcs[count];
+
+  /* Create the sorted policy_hash_codes */
+  {
+    size_t idx[count];
+    for (size_t i = 0; i < count; i++)
+      idx[i] = i;
+
+    /* Sort the indices according to the hash codes of the corresponding
+     * details. */
+    qsort_r (idx,
+             count,
+             sizeof(size_t),
+             hash_code_cmp,
+             fulfillment->details);
+
+    /* Finally, concatenate all hash_codes in sorted order */
+    for (size_t i = 0; i < count; i++)
+      hcs[i] = fulfillment->details[idx[i]].hash_code;
+  }
+
+
+  /* Now, add the proof to the policy_fulfillments table, retrieve the
+   * record_id */
+  {
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp),
+      TALER_PQ_query_param_json (fulfillment->proof),
+      GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof),
+      GNUNET_PQ_query_param_fixed_size (hcs,
+                                        count * sizeof(struct 
GNUNET_HashCode)),
+      GNUNET_PQ_query_param_end
+    };
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
+                                    &fulfillment->fulfillment_id),
+      GNUNET_PQ_result_spec_end
+    };
+
+    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   
"insert_proof_into_policy_fulfillments",
+                                                   params,
+                                                   rs);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      return qs;
+  }
+
+  /* Now, set the states of each entry corresponding to the hash_codes in
+   * policy_details accordingly */
+  for (size_t i = 0; i < count; i++)
+  {
+    struct TALER_PolicyDetails *pos = &fulfillment->details[i];
+    {
+      struct GNUNET_PQ_QueryParam params[] = {
+        GNUNET_PQ_query_param_auto_from_type (&pos->hash_code),
+        GNUNET_PQ_query_param_timestamp (&pos->deadline),
+        TALER_PQ_query_param_amount (&pos->commitment),
+        TALER_PQ_query_param_amount (&pos->accumulated_total),
+        TALER_PQ_query_param_amount (&pos->policy_fee),
+        TALER_PQ_query_param_amount (&pos->transferable_amount),
+        GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state),
+        GNUNET_PQ_query_param_end
+      };
+
+      qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                               "update_policy_details",
+                                               params);
+      if (qs < 0)
+        return qs;
+    }
+  }
+
+  return qs;
+}
+
+
 /**
  * Get the balance of the specified reserve.
  *
@@ -10465,8 +10672,8 @@ postgres_delete_shard_locks (void *cls)
 
 
 /**
- * Function called to save the configuration of an extension
- * (age-restriction, peer2peer, ...).  After successful storage of the
+ * Function called to save the manifest of an extension
+ * (age-restriction, policy_extension_...) After successful storage of the
  * configuration it triggers the corresponding event.
  *
  * @param cls the @e cls of this struct with the plugin-specific state
@@ -10475,15 +10682,15 @@ postgres_delete_shard_locks (void *cls)
  * @return transaction status code
  */
 enum GNUNET_DB_QueryStatus
-postgres_set_extension_config (void *cls,
-                               const char *extension_name,
-                               const char *config)
+postgres_set_extension_manifest (void *cls,
+                                 const char *extension_name,
+                                 const char *manifest)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam pcfg =
-    (NULL == config || 0 == *config)
+    (NULL == manifest || 0 == *manifest)
     ? GNUNET_PQ_query_param_null ()
-    : GNUNET_PQ_query_param_string (config);
+    : GNUNET_PQ_query_param_string (manifest);
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (extension_name),
     pcfg,
@@ -10491,24 +10698,24 @@ postgres_set_extension_config (void *cls,
   };
 
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "set_extension_config",
+                                             "set_extension_manifest",
                                              params);
 }
 
 
 /**
- * Function called to get the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to get the manifest of an extension
+ * (age-restriction, policy_extension_...)
  *
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param extension_name the name of the extension
- * @param[out] config JSON object of the configuration as string
+ * @param[out] manifest JSON object of the manifest as string
  * @return transaction status code
  */
 enum GNUNET_DB_QueryStatus
-postgres_get_extension_config (void *cls,
-                               const char *extension_name,
-                               char **config)
+postgres_get_extension_manifest (void *cls,
+                                 const char *extension_name,
+                                 char **manifest)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -10518,15 +10725,15 @@ postgres_get_extension_config (void *cls,
   bool is_null;
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_allow_null (
-      GNUNET_PQ_result_spec_string ("config",
-                                    config),
+      GNUNET_PQ_result_spec_string ("manifest",
+                                    manifest),
       &is_null),
     GNUNET_PQ_result_spec_end
   };
 
-  *config = NULL;
+  *manifest = NULL;
   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "get_extension_config",
+                                                   "get_extension_manifest",
                                                    params,
                                                    rs);
 }
@@ -12311,8 +12518,11 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->get_withdraw_info = &postgres_get_withdraw_info;
   plugin->do_withdraw = &postgres_do_withdraw;
   plugin->do_batch_withdraw = &postgres_do_batch_withdraw;
+  plugin->get_policy_details = &postgres_get_policy_details;
+  plugin->persist_policy_details = &postgres_persist_policy_details;
   plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert;
   plugin->do_deposit = &postgres_do_deposit;
+  plugin->add_policy_fulfillment_proof = 
&postgres_add_policy_fulfillment_proof;
   plugin->do_melt = &postgres_do_melt;
   plugin->do_refund = &postgres_do_refund;
   plugin->do_recoup = &postgres_do_recoup;
@@ -12446,10 +12656,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &postgres_release_revolving_shard;
   plugin->delete_shard_locks
     = &postgres_delete_shard_locks;
-  plugin->set_extension_config
-    = &postgres_set_extension_config;
-  plugin->get_extension_config
-    = &postgres_get_extension_config;
+  plugin->set_extension_manifest
+    = &postgres_set_extension_manifest;
+  plugin->get_extension_manifest
+    = &postgres_get_extension_manifest;
   plugin->insert_partner
     = &postgres_insert_partner;
   plugin->insert_contract
diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql
index e9075775..30610e86 100644
--- a/src/exchangedb/procedures.sql
+++ b/src/exchangedb/procedures.sql
@@ -510,8 +510,8 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit(
   IN in_coin_pub BYTEA,
   IN in_coin_sig BYTEA,
   IN in_shard INT8,
-  IN in_extension_blocked BOOLEAN,
-  IN in_extension_details VARCHAR,
+  IN in_policy_blocked BOOLEAN,
+  IN in_policy_details_serial_id INT8,
   OUT out_exchange_timestamp INT8,
   OUT out_balance_ok BOOLEAN,
   OUT out_conflict BOOLEAN)
@@ -519,26 +519,12 @@ LANGUAGE plpgsql
 AS $$
 DECLARE
   wtsi INT8; -- wire target serial id
-DECLARE
-  xdi INT8; -- eXstension details serial id
 BEGIN
 -- Shards: INSERT extension_details (by extension_details_serial_id)
 --         INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING;
 --         INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING;
 --         UPDATE known_coins (by coin_pub)
 
-IF NOT NULL in_extension_details
-THEN
-  INSERT INTO exchange.extension_details
-  (extension_options)
-  VALUES
-    (in_extension_details)
-  RETURNING extension_details_serial_id INTO xdi;
-ELSE
-  xdi=NULL;
-END IF;
-
-
 INSERT INTO exchange.wire_targets
   (wire_target_h_payto
   ,payto_uri)
@@ -572,8 +558,8 @@ INSERT INTO exchange.deposits
   ,coin_sig
   ,wire_salt
   ,wire_target_h_payto
-  ,extension_blocked
-  ,extension_details_serial_id
+  ,policy_blocked
+  ,policy_details_serial_id
   )
   VALUES
   (in_shard
@@ -590,8 +576,8 @@ INSERT INTO exchange.deposits
   ,in_coin_sig
   ,in_wire_salt
   ,in_h_payto
-  ,in_extension_blocked
-  ,xdi)
+  ,in_policy_blocked
+  ,in_policy_details_serial_id)
   ON CONFLICT DO NOTHING;
 
 IF NOT FOUND
@@ -611,6 +597,7 @@ THEN
      AND wire_target_h_payto=in_h_payto
      AND coin_pub=in_coin_pub
      AND coin_sig=in_coin_sig;
+     -- AND policy_details_serial_id=in_policy_details_serial_id; -- FIXME: is 
this required for idempotency?
 
   IF NOT FOUND
   THEN
@@ -2420,5 +2407,117 @@ RETURN;
 
 END $$;
 
+CREATE OR REPLACE FUNCTION insert_or_update_policy_details(
+  IN in_policy_hash_code BYTEA,
+  IN in_policy_json VARCHAR,
+  IN in_deadline INT8,
+  IN in_commitment_val INT8,
+  IN in_commitment_frac INT4,
+  IN in_accumulated_total_val INT8,
+  IN in_accumulated_total_frac INT4,
+  IN in_fee_val INT8,
+  IN in_fee_frac INT4,
+  IN in_transferable_val INT8,
+  IN in_transferable_frac INT4,
+  IN in_fulfillment_state SMALLINT,
+  OUT out_policy_details_serial_id INT8,
+  OUT out_accumulated_total_val INT8,
+  OUT out_accumulated_total_frac INT4,
+  OUT out_fulfillment_state SMALLINT)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+       cur_commitment_val INT8;
+       cur_commitment_frac INT4;
+       cur_accumulated_total_val INT8;
+       cur_accumulated_total_frac INT4;
+BEGIN
+       -- First, try to create a new entry.
+       INSERT INTO policy_details
+               (policy_hash_code,
+                policy_json,
+                deadline,
+                commitment_val,
+                commitment_frac,
+                accumulated_total_val,
+                accumulated_total_frac,
+                fee_val,
+                fee_frac,
+                transferable_val,
+                transferable_frac,
+                fulfillment_state)
+       VALUES (in_policy_hash_code,
+                in_policy_json,
+                in_deadline,
+                in_commitment_val,
+                in_commitment_frac,
+                in_accumulated_total_val,
+                in_accumulated_total_frac,
+                in_fee_val,
+                in_fee_frac,
+                in_transferable_val,
+                in_transferable_frac,
+                in_fulfillment_state)
+       ON CONFLICT (policy_hash_code) DO NOTHING
+       RETURNING policy_details_serial_id INTO out_policy_details_serial_id;
+
+       -- If the insert was successful, return
+       -- We assume that the fullfilment_state was correct in first place.
+       IF FOUND THEN
+               out_accumulated_total_val  = in_accumulated_total_val;
+               out_accumulated_total_frac = in_accumulated_total_frac;
+               out_fulfillment_state      = in_fulfillment_state;
+               RETURN;
+       END IF;
+
+       -- We had a conflict, grab the parts we need to update.
+       SELECT policy_details_serial_id,
+               commitment_val,
+               commitment_frac,
+               accumulated_total_val,
+               accumulated_total_frac
+       INTO out_policy_details_serial_id,
+               cur_commitment_val,
+               cur_commitment_frac,
+               cur_accumulated_total_val,
+               cur_accumulated_total_frac
+       FROM policy_details
+       WHERE policy_hash_code = in_policy_hash_code;
+
+       -- calculate the new values (overflows throws exception)
+       out_accumulated_total_val  = cur_accumulated_total_val  + 
in_accumulated_total_val;
+       out_accumulated_total_frac = cur_accumulated_total_frac + 
in_accumulated_total_frac;
+       -- normalize
+       out_accumulated_total_val = out_accumulated_total_val + 
out_accumulated_total_frac / 100000000;
+       out_accumulated_total_frac = out_accumulated_total_frac % 100000000;
+
+       IF (out_accumulated_total_val > (1 << 52))
+       THEN
+               RAISE EXCEPTION 'accumulation overflow';
+       END IF;
+
+
+       -- Set the fulfillment_state according to the values.
+       -- For now, we only update the state when it was INSUFFICIENT.
+       -- FIXME: What to do in case of Failure or other state?
+       IF (out_fullfillment_state = 1) -- INSUFFICIENT
+       THEN
+               IF (out_accumulated_total_val >= cur_commitment_val OR
+                       (out_accumulated_total_val = cur_commitment_val AND
+                               out_accumulated_total_frac >= 
cur_commitment_frac))
+               THEN
+                       out_fulfillment_state = 2; -- READY
+               END IF;
+       END IF;
+
+       -- Now, update the record
+       UPDATE exchange.policy_details
+       SET
+               accumulated_val  = out_accumulated_total_val,
+               accumulated_frac = out_accumulated_total_frac,
+               fulfillment_state = out_fulfillment_state
+       WHERE
+               policy_details_serial_id = out_policy_details_serial_id;
+END $$;
 
 COMMIT;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index eb990412..111ee936 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -112,53 +112,53 @@ mark_prepare_cb (void *cls,
  * Simple check that config retrieval and setting for extensions work
  */
 static enum GNUNET_GenericReturnValue
-test_extension_config (void)
+test_extension_manifest (void)
 {
-  char *config;
+  char *manifest;
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
-          plugin->get_extension_config (plugin->cls,
-                                        "fnord",
-                                        &config));
+          plugin->get_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          &manifest));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->set_extension_config (plugin->cls,
-                                        "fnord",
-                                        "bar"));
+          plugin->set_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          "bar"));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_extension_config (plugin->cls,
-                                        "fnord",
-                                        &config));
+          plugin->get_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          &manifest));
 
-  FAILIF (0 != strcmp ("bar", config));
-  GNUNET_free (config);
+  FAILIF (0 != strcmp ("bar", manifest));
+  GNUNET_free (manifest);
 
   /* let's do this again! */
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->set_extension_config (plugin->cls,
-                                        "fnord",
-                                        "buzz"));
+          plugin->set_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          "buzz"));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_extension_config (plugin->cls,
-                                        "fnord",
-                                        &config));
+          plugin->get_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          &manifest));
 
-  FAILIF (0 != strcmp ("buzz", config));
-  GNUNET_free (config);
+  FAILIF (0 != strcmp ("buzz", manifest));
+  GNUNET_free (manifest);
 
   /* let's do this again, with NULL */
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->set_extension_config (plugin->cls,
-                                        "fnord",
-                                        NULL));
+          plugin->set_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          NULL));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_extension_config (plugin->cls,
-                                        "fnord",
-                                        &config));
+          plugin->get_extension_manifest (plugin->cls,
+                                          "fnord",
+                                          &manifest));
 
-  FAILIF (NULL != config);
+  FAILIF (NULL != manifest);
 
   return GNUNET_OK;
 drop:
@@ -1269,7 +1269,7 @@ run (void *cls)
                                                  NULL));
   /* simple extension check */
   FAILIF (GNUNET_OK !=
-          test_extension_config ());
+          test_extension_manifest ());
 
   RND_BLK (&reserve_pub);
   GNUNET_assert (GNUNET_OK ==
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am
index 5d4ed128..c867a951 100644
--- a/src/extensions/Makefile.am
+++ b/src/extensions/Makefile.am
@@ -11,7 +11,7 @@ if USE_COVERAGE
 endif
 
 
-# Libraries
+# Basic extension handling library
 
 lib_LTLIBRARIES = \
   libtalerextensions.la
@@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \
 
 libtalerextensions_la_SOURCES = \
   extensions.c \
-  extension_age_restriction.c
+  age_restriction_helper.c
 
 libtalerextensions_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
@@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \
   -lgnunetutil \
   -ljansson \
   $(XLIB)
+
diff --git a/src/extensions/Makefile.am 
b/src/extensions/age_restriction/Makefile.am
similarity index 57%
copy from src/extensions/Makefile.am
copy to src/extensions/age_restriction/Makefile.am
index 5d4ed128..85d67653 100644
--- a/src/extensions/Makefile.am
+++ b/src/extensions/age_restriction/Makefile.am
@@ -10,21 +10,21 @@ if USE_COVERAGE
   XLIB = -lgcov
 endif
 
+# Age restriction as extension library
 
-# Libraries
+plugindir = $(libdir)/taler
 
-lib_LTLIBRARIES = \
-  libtalerextensions.la
+plugin_LTLIBRARIES = \
+  libtaler_extension_age_restriction.la
 
-libtalerextensions_la_LDFLAGS = \
+libtaler_extension_age_restriction_la_LDFLAGS = \
   -version-info 0:0:0 \
   -no-undefined
 
-libtalerextensions_la_SOURCES = \
-  extensions.c \
-  extension_age_restriction.c
+libtaler_extension_age_restriction_la_SOURCES = \
+  age_restriction.c
 
-libtalerextensions_la_LIBADD = \
+libtaler_extension_age_restriction_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
   -lgnunetjson \
diff --git a/src/extensions/age_restriction/age_restriction.c 
b/src/extensions/age_restriction/age_restriction.c
new file mode 100644
index 00000000..58124250
--- /dev/null
+++ b/src/extensions/age_restriction/age_restriction.c
@@ -0,0 +1,258 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2021-2022 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 age_restriction.c
+ * @brief Utility functions regarding age restriction
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+/* ==================================================
+ *
+ * Age Restriction  TALER_Extension implementation
+ *
+ * ==================================================
+ */
+
+/**
+ * @brief local configuration
+ */
+
+static struct TALER_AgeRestrictionConfig AR_config = {0};
+
+/**
+ * @brief implements the TALER_Extension.disable interface.
+ *
+ * @param ext Pointer to the current extension
+ */
+static void
+age_restriction_disable (
+  struct TALER_Extension *ext)
+{
+  if (NULL == ext)
+    return;
+
+  ext->enabled = false;
+  ext->config = NULL;
+
+  AR_config.mask.bits = 0;
+  AR_config.num_groups = 0;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_config interface.
+ *
+ * @param ext if NULL, only tests the configuration
+ * @param jconfig the configuration as json
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_config (
+  struct TALER_Extension *ext,
+  json_t *jconfig)
+{
+  struct TALER_AgeMask mask = {0};
+  enum GNUNET_GenericReturnValue ret;
+
+  ret = TALER_JSON_parse_age_groups (jconfig, &mask);
+  if (GNUNET_OK != ret)
+    return ret;
+
+  /* only testing the parser */
+  if (ext == NULL)
+    return GNUNET_OK;
+
+  if (TALER_Extension_AgeRestriction != ext->type)
+    return GNUNET_SYSERR;
+
+  if (mask.bits > 0)
+  {
+    /* if the mask is not zero, the first bit MUST be set */
+    if (0 == (mask.bits & 1))
+      return GNUNET_SYSERR;
+
+    AR_config.mask.bits = mask.bits;
+    AR_config.num_groups = __builtin_popcount (mask.bits) - 1;
+  }
+
+  ext->config = &AR_config;
+  ext->enabled = true;
+  json_decref (jconfig);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "loaded new age restriction config with age groups: %s\n",
+              TALER_age_mask_to_string (&mask));
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.manifest interface.
+ *
+ * @param ext if NULL, only tests the configuration
+ * @return configuration as json_t* object, maybe NULL
+ */
+static json_t *
+age_restriction_manifest (
+  const struct TALER_Extension *ext)
+{
+  char *mask_str;
+  json_t *conf;
+
+  GNUNET_assert (NULL != ext);
+
+  if (NULL == ext->config)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "age restriction not configured");
+    return json_null ();
+  }
+
+  mask_str = TALER_age_mask_to_string (&AR_config.mask);
+  conf = GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_string ("age_groups", mask_str)
+    );
+
+  free (mask_str);
+
+  return GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_bool ("critical", ext->critical),
+    GNUNET_JSON_pack_string ("version", ext->version),
+    GNUNET_JSON_pack_object_steal ("config", conf)
+    );
+}
+
+
+/* The extension for age restriction */
+struct TALER_Extension TE_age_restriction = {
+  .type = TALER_Extension_AgeRestriction,
+  .name = "age_restriction",
+  .critical = false,
+  .version = "1",
+  .enabled = false, /* disabled per default */
+  .config = NULL,
+  .disable = &age_restriction_disable,
+  .load_config = &age_restriction_load_config,
+  .manifest = &age_restriction_manifest,
+
+  /* This extension is not a policy extension */
+  .create_policy_details = NULL,
+  .policy_get_handler = NULL,
+  .policy_post_handler = NULL,
+};
+
+
+/**
+ * @brief implements the init() function for GNUNET_PLUGIN_load
+ *
+ * @param arg Pointer to the GNUNET_CONFIGURATION_Handle
+ * @return pointer to TALER_Extension on success or NULL otherwise.
+ */
+void *
+libtaler_extension_age_restriction_init (void *arg)
+{
+  const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
+  char *groups = NULL;
+  struct TALER_AgeMask mask = {0};
+
+  if ((GNUNET_YES !=
+       GNUNET_CONFIGURATION_have_value (cfg,
+                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                        "ENABLED"))
+      ||
+      (GNUNET_YES !=
+       GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                             
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                             "ENABLED")))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "[age restriction] no section %s found in configuration\n",
+                TALER_EXTENSION_SECTION_AGE_RESTRICTION);
+
+    return NULL;
+  }
+
+  /* Age restriction is enabled, extract age groups */
+  if ((GNUNET_YES ==
+       GNUNET_CONFIGURATION_have_value (cfg,
+                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                        "AGE_GROUPS"))
+      &&
+      (GNUNET_YES !=
+       GNUNET_CONFIGURATION_get_value_string (cfg,
+                                              
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                              "AGE_GROUPS",
+                                              &groups)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "[age restriction] AGE_GROUPS in %s is not a string\n",
+                TALER_EXTENSION_SECTION_AGE_RESTRICTION);
+
+    return NULL;
+  }
+
+  mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+
+  if ((groups != NULL) &&
+      (GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "[age restriction] couldn't parse age groups: '%s'\n",
+                groups);
+    return NULL;
+  }
+
+  AR_config.mask = mask;
+  AR_config.num_groups = __builtin_popcount (mask.bits) - 1;   /* no 
underflow, first bit always set */
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "[age restriction] setting age mask to %s with #groups: %d\n",
+              TALER_age_mask_to_string (&AR_config.mask),
+              __builtin_popcount (AR_config.mask.bits) - 1);
+
+  TE_age_restriction.config = &AR_config;
+
+  /* Note: we do now have TE_age_restriction_config set, however the extension
+   * is not yet enabled! For age restriction to become active, load_config must
+   * have been called. */
+
+  GNUNET_free (groups);
+  return &TE_age_restriction;
+}
+
+
+/**
+ * @brief implements the done() function for GNUNET_PLUGIN_load
+ *
+ * @param cfg unsued
+ * @return pointer to TALER_Extension on success or NULL otherwise.
+ */
+void *
+libtaler_extension_age_restriction_done (void *arg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "[age restriction] disabling and unloading");
+  AR_config.mask.bits = 0;
+  AR_config.num_groups = 0;
+  return NULL;
+}
+
+
+/* end of age_restriction.c */
diff --git a/src/extensions/age_restriction_helper.c 
b/src/extensions/age_restriction_helper.c
new file mode 100644
index 00000000..8ba83511
--- /dev/null
+++ b/src/extensions/age_restriction_helper.c
@@ -0,0 +1,73 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022- 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 age_restriction_helper.c
+ * @brief Helper functions for age restriction
+ * @author Özgür Kesim
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ()
+{
+  const struct TALER_Extension *ext;
+
+  ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+  if (NULL == ext)
+    return NULL;
+
+  return ext->config;
+}
+
+
+bool
+TALER_extensions_is_age_restriction_enabled ()
+{
+  const struct TALER_Extension *ext;
+
+  ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+  if (NULL == ext)
+    return false;
+
+  return ext->enabled;
+}
+
+
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ()
+{
+  const struct TALER_Extension *ext;
+  const struct TALER_AgeRestrictionConfig *conf;
+
+  ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+
+  if ((NULL == ext) ||
+      (NULL == ext->config))
+    return (struct TALER_AgeMask) {0}
+  ;
+
+  conf = ext->config;
+  return conf->mask;
+}
+
+
+/* end age_restriction_helper.c */
diff --git a/src/extensions/extension_age_restriction.c 
b/src/extensions/extension_age_restriction.c
deleted file mode 100644
index 00a03841..00000000
--- a/src/extensions/extension_age_restriction.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
-   This file is part of TALER
-   Copyright (C) 2021-2022 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 extension_age_restriction.c
- * @brief Utility functions regarding age restriction
- * @author Özgür Kesim
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_extensions.h"
-#include "stdint.h"
-
-/**
- * Carries all the information we need for age restriction
- */
-struct age_restriction_config
-{
-  struct TALER_AgeMask mask;
-  size_t num_groups;
-};
-
-/**
- * Global config for this extension
- */
-static struct age_restriction_config TE_age_restriction_config = {0};
-
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
-  const char *groups,
-  struct TALER_AgeMask *mask)
-{
-
-  const char *pos = groups;
-  unsigned int prev = 0;
-  unsigned int val = 0;
-  char c;
-
-  while (*pos)
-  {
-    c = *pos++;
-    if (':' == c)
-    {
-      if (prev >= val)
-        return GNUNET_SYSERR;
-
-      mask->bits |= 1 << val;
-      prev = val;
-      val = 0;
-      continue;
-    }
-
-    if ('0'>c || '9'<c)
-      return GNUNET_SYSERR;
-
-    val = 10 * val + c - '0';
-
-    if (0>=val || 32<=val)
-      return GNUNET_SYSERR;
-  }
-
-  if (32<=val || prev>=val)
-    return GNUNET_SYSERR;
-
-  mask->bits |= (1 << val);
-  mask->bits |= 1; // mark zeroth group, too
-
-  return GNUNET_OK;
-}
-
-
-char *
-TALER_age_mask_to_string (
-  const struct TALER_AgeMask *mask)
-{
-  uint32_t bits = mask->bits;
-  unsigned int n = 0;
-  char *buf = GNUNET_malloc (32 * 3); // max characters possible
-  char *pos = buf;
-
-  if (NULL == buf)
-  {
-    return buf;
-  }
-
-  while (bits != 0)
-  {
-    bits >>= 1;
-    n++;
-    if (0 == (bits & 1))
-    {
-      continue;
-    }
-
-    if (n > 9)
-    {
-      *(pos++) = '0' + n / 10;
-    }
-    *(pos++) = '0' + n % 10;
-
-    if (0 != (bits >> 1))
-    {
-      *(pos++) = ':';
-    }
-  }
-  return buf;
-}
-
-
-/* ==================================================
- *
- * Age Restriction  TALER_Extension implementation
- *
- * ==================================================
- */
-
-/**
- * @brief implements the TALER_Extension.disable interface.
- *
- * @param ext Pointer to the current extension
- */
-static void
-age_restriction_disable (
-  struct TALER_Extension *ext)
-{
-  if (NULL == ext)
-    return;
-
-  ext->config = NULL;
-
-  if (NULL != ext->config_json)
-  {
-    json_decref (ext->config_json);
-    ext->config_json = NULL;
-  }
-
-  TE_age_restriction_config.mask.bits = 0;
-  TE_age_restriction_config.num_groups = 0;
-}
-
-
-/**
- * @brief implements the TALER_Extension.load_taler_config interface.
- *
- * @param ext Pointer to the current extension
- * @param cfg Handle to the GNUNET configuration
- * @return Error if extension for age restriction was set, but age groups were
- *         invalid, OK otherwise.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_load_taler_config (
-  struct TALER_Extension *ext,
-  const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  char *groups = NULL;
-  enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
-  struct TALER_AgeMask mask = {0};
-
-  if ((GNUNET_YES !=
-       GNUNET_CONFIGURATION_have_value (cfg,
-                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                        "ENABLED"))
-      ||
-      (GNUNET_YES !=
-       GNUNET_CONFIGURATION_get_value_yesno (cfg,
-                                             
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                             "ENABLED")))
-  {
-    /* Age restriction is not enabled */
-    ext->config = NULL;
-    ext->config_json = NULL;
-    return GNUNET_OK;
-  }
-
-  /* Age restriction is enabled, extract age groups */
-  if ((GNUNET_YES ==
-       GNUNET_CONFIGURATION_have_value (cfg,
-                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                        "AGE_GROUPS"))
-      &&
-      (GNUNET_YES !=
-       GNUNET_CONFIGURATION_get_value_string (cfg,
-                                              
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                              "AGE_GROUPS",
-                                              &groups)))
-    return GNUNET_SYSERR;
-
-
-  mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
-  ret = GNUNET_OK;
-
-  if (groups != NULL)
-  {
-    ret = TALER_parse_age_group_string (groups, &mask);
-    if (GNUNET_OK != ret)
-      mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
-  }
-
-  if (GNUNET_OK == ret)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "setting age mask to %x with #groups: %d\n", mask.bits,
-                __builtin_popcount (mask.bits) - 1);
-    TE_age_restriction_config.mask.bits = mask.bits;
-    TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; 
/* no underflow, first bit always set */
-    ext->config = &TE_age_restriction_config;
-
-    /* Note: we do now have TE_age_restriction_config set, however
-     * ext->config_json is NOT set, i.e. the extension is not yet active! For
-     * age restriction to become active, load_json_config must have been
-     * called. */
-  }
-
-
-  GNUNET_free (groups);
-  return ret;
-}
-
-
-/**
- * @brief implements the TALER_Extension.load_json_config interface.
- *
- * @param ext if NULL, only tests the configuration
- * @param jconfig the configuration as json
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_load_json_config (
-  struct TALER_Extension *ext,
-  json_t *jconfig)
-{
-  struct TALER_AgeMask mask = {0};
-  enum GNUNET_GenericReturnValue ret;
-
-  ret = TALER_JSON_parse_age_groups (jconfig, &mask);
-  if (GNUNET_OK != ret)
-    return ret;
-
-  /* only testing the parser */
-  if (ext == NULL)
-    return GNUNET_OK;
-
-  if (TALER_Extension_AgeRestriction != ext->type)
-    return GNUNET_SYSERR;
-
-  TE_age_restriction_config.mask.bits = mask.bits;
-  TE_age_restriction_config.num_groups = 0;
-
-  if (mask.bits > 0)
-  {
-    /* if the mask is not zero, the first bit MUST be set */
-    if (0 == (mask.bits & 1))
-      return GNUNET_SYSERR;
-
-    TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
-  }
-
-  ext->config = &TE_age_restriction_config;
-
-  if (NULL != ext->config_json)
-    json_decref (ext->config_json);
-
-  ext->config_json = jconfig;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-              "loaded new age restriction config with age groups: %s\n",
-              TALER_age_mask_to_string (&mask));
-
-  return GNUNET_OK;
-}
-
-
-/**
- * @brief implements the TALER_Extension.config_to_json interface.
- *
- * @param ext if NULL, only tests the configuration
- * @return configuration as json_t* object, maybe NULL
- */
-static json_t *
-age_restriction_config_to_json (
-  const struct TALER_Extension *ext)
-{
-  char *mask_str;
-  json_t *conf;
-
-  GNUNET_assert (NULL != ext);
-
-  if (NULL == ext->config)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "age restriction not configured");
-    return json_null ();
-  }
-
-  if (NULL != ext->config_json)
-  {
-    return json_copy (ext->config_json);
-  }
-
-  mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
-  conf = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_string ("age_groups", mask_str)
-    );
-
-  return GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_bool ("critical", ext->critical),
-    GNUNET_JSON_pack_string ("version", ext->version),
-    GNUNET_JSON_pack_object_steal ("config", conf)
-    );
-}
-
-
-/**
- * @brief implements the TALER_Extension.test_json_config interface.
- *
- * @param config configuration as json_t* to test
- * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_test_json_config (
-  const json_t *config)
-{
-  struct TALER_AgeMask mask = {0};
-
-  return TALER_JSON_parse_age_groups (config, &mask);
-}
-
-
-/* The extension for age restriction */
-struct TALER_Extension TE_age_restriction = {
-  .next = NULL,
-  .type = TALER_Extension_AgeRestriction,
-  .name = "age_restriction",
-  .critical = false,
-  .version = "1",
-  .config = NULL,   // disabled per default
-  .config_json = NULL,
-  .disable = &age_restriction_disable,
-  .test_json_config = &age_restriction_test_json_config,
-  .load_json_config = &age_restriction_load_json_config,
-  .config_to_json = &age_restriction_config_to_json,
-  .load_taler_config = &age_restriction_load_taler_config,
-};
-
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ()
-{
-  return TALER_extensions_add (&TE_age_restriction);
-}
-
-
-bool
-TALER_extensions_age_restriction_is_configured ()
-{
-  return (0 != TE_age_restriction_config.mask.bits);
-}
-
-
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ()
-{
-  return TE_age_restriction_config.mask;
-}
-
-
-size_t
-TALER_extensions_age_restriction_num_groups ()
-{
-  return TE_age_restriction_config.num_groups;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
-                             struct TALER_AgeMask *mask)
-{
-  enum GNUNET_GenericReturnValue ret;
-  const char *str;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_string ("age_groups",
-                             &str),
-    GNUNET_JSON_spec_end ()
-  };
-
-  ret = GNUNET_JSON_parse (root,
-                           spec,
-                           NULL,
-                           NULL);
-  if (GNUNET_OK == ret)
-    TALER_parse_age_group_string (str, mask);
-
-  GNUNET_JSON_parse_free (spec);
-
-  return ret;
-}
-
-
-/* end of extension_age_restriction.c */
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
index 0df0bae3..2ed973d9 100644
--- a/src/extensions/extensions.c
+++ b/src/extensions/extensions.c
@@ -24,51 +24,53 @@
 #include "taler_extensions.h"
 #include "stdint.h"
 
-
 /* head of the list of all registered extensions */
-static struct TALER_Extension *TE_extensions = NULL;
-
+static struct TALER_Extensions TE_extensions = {
+  .next = NULL,
+  .extension = NULL,
+};
 
-const struct TALER_Extension *
+const struct TALER_Extensions *
 TALER_extensions_get_head ()
 {
-  return TE_extensions;
+  return &TE_extensions;
 }
 
 
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
-  struct TALER_Extension *extension)
+static enum GNUNET_GenericReturnValue
+add_extension (
+  const struct TALER_Extension *extension)
 {
   /* Sanity checks */
   if ((NULL == extension) ||
       (NULL == extension->name) ||
       (NULL == extension->version) ||
       (NULL == extension->disable) ||
-      (NULL == extension->test_json_config) ||
-      (NULL == extension->load_json_config) ||
-      (NULL == extension->config_to_json) ||
-      (NULL == extension->load_taler_config))
+      (NULL == extension->load_config) ||
+      (NULL == extension->manifest))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "invalid extension\n");
     return GNUNET_SYSERR;
   }
 
-  if (NULL == TE_extensions) /* first extension ?*/
-    TE_extensions = (struct TALER_Extension *) extension;
+  if (NULL == TE_extensions.extension) /* first extension ?*/
+    TE_extensions.extension = extension;
   else
   {
-    struct TALER_Extension *iter;
-    struct TALER_Extension *last;
+    struct TALER_Extensions *iter;
+    struct TALER_Extensions *last;
 
     /* Check for collisions */
-    for (iter = TE_extensions; NULL != iter; iter = iter->next)
+    for (iter = &TE_extensions;
+         NULL != iter && NULL != iter->extension;
+         iter = iter->next)
     {
+      const struct TALER_Extension *ext = iter->extension;
       last = iter;
-      if (extension->type == iter->type ||
+      if (extension->type == ext->type ||
           0 == strcasecmp (extension->name,
-                           iter->name))
+                           ext->name))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "extension collision for `%s'\n",
@@ -78,7 +80,11 @@ TALER_extensions_add (
     }
 
     /* No collisions found, so add this extension to the list */
-    last->next = extension;
+    {
+      struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
+      extn->extension = extension;
+      last->next = extn;
+    }
   }
 
   return GNUNET_OK;
@@ -89,12 +95,12 @@ const struct TALER_Extension *
 TALER_extensions_get_by_type (
   enum TALER_Extension_Type type)
 {
-  for (const struct TALER_Extension *it = TE_extensions;
-       NULL != it;
+  for (const struct TALER_Extensions *it = &TE_extensions;
+       NULL != it && NULL != it->extension;
        it = it->next)
   {
-    if (it->type == type)
-      return it;
+    if (it->extension->type == type)
+      return it->extension;
   }
 
   /* No extension found. */
@@ -109,8 +115,7 @@ TALER_extensions_is_enabled_type (
   const struct TALER_Extension *ext =
     TALER_extensions_get_by_type (type);
 
-  return (NULL != ext &&
-          TALER_extensions_is_enabled (ext));
+  return (NULL != ext && ext->enabled);
 }
 
 
@@ -118,33 +123,34 @@ const struct TALER_Extension *
 TALER_extensions_get_by_name (
   const char *name)
 {
-  for (const struct TALER_Extension *it = TE_extensions;
+  for (const struct TALER_Extensions *it = &TE_extensions;
        NULL != it;
        it = it->next)
   {
-    if (0 == strcasecmp (name, it->name))
-      return it;
+    if (0 == strcasecmp (name, it->extension->name))
+      return it->extension;
   }
-  /* No extension found. */
+  /* No extension found, try to load it. */
+
   return NULL;
 }
 
 
 enum GNUNET_GenericReturnValue
-TALER_extensions_verify_json_config_signature (
-  json_t *extensions,
+TALER_extensions_verify_manifests_signature (
+  json_t *manifests,
   struct TALER_MasterSignatureP *extensions_sig,
   struct TALER_MasterPublicKeyP *master_pub)
 {
-  struct TALER_ExtensionConfigHashP h_config;
+  struct TALER_ExtensionManifestsHashP h_manifests;
 
   if (GNUNET_OK !=
-      TALER_JSON_extensions_config_hash (extensions,
-                                         &h_config))
+      TALER_JSON_extensions_manifests_hash (manifests,
+                                            &h_manifests))
     return GNUNET_SYSERR;
   if (GNUNET_OK !=
-      TALER_exchange_offline_extension_config_hash_verify (
-        &h_config,
+      TALER_exchange_offline_extension_manifests_hash_verify (
+        &h_manifests,
         master_pub,
         extensions_sig))
     return GNUNET_NO;
@@ -178,7 +184,8 @@ configure_extension (
 {
   struct LoadConfClosure *col = cls;
   const char *name;
-  const struct TALER_Extension *extension;
+  char *lib_name;
+  struct TALER_Extension *extension;
 
   if (GNUNET_OK != col->error)
     return;
@@ -190,33 +197,49 @@ configure_extension (
 
   name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
 
-  if (NULL ==
-      (extension = TALER_extensions_get_by_name (name)))
+
+  /* Load the extension library */
+  GNUNET_asprintf (&lib_name,
+                   "libtaler_extension_%s",
+                   name);
+  extension = GNUNET_PLUGIN_load (
+    lib_name,
+    (void *) col->cfg);
+  if (NULL == extension)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unsupported extension `%s` (section [%s]).\n", name,
+                "Couldn't load extension library to `%s` (section [%s]).\n",
+                name,
                 section);
     col->error = GNUNET_SYSERR;
     return;
   }
 
-  if (GNUNET_OK !=
-      extension->load_taler_config (
-        (struct TALER_Extension *) extension,
-        col->cfg))
+
+  if (GNUNET_OK != add_extension (extension))
   {
+    /* TODO: Ignoring return values here */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Couldn't parse configuration for extension `%s` (section 
[%s]).\n",
+                "Couldn't add extension `%s` (section [%s]).\n",
                 name,
                 section);
     col->error = GNUNET_SYSERR;
+    GNUNET_PLUGIN_unload (
+      lib_name,
+      (void *) col->cfg);
     return;
   }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "extension library '%s' loaded\n",
+              lib_name);
 }
 
 
+static bool extensions_loaded = false;
+
 enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_init (
   const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   struct LoadConfClosure col = {
@@ -224,15 +247,22 @@ TALER_extensions_load_taler_config (
     .error = GNUNET_OK,
   };
 
+  if (extensions_loaded)
+    return GNUNET_OK;
+
   GNUNET_CONFIGURATION_iterate_sections (cfg,
                                          &configure_extension,
                                          &col);
+
+  if (GNUNET_OK == col.error)
+    extensions_loaded = true;
+
   return col.error;
 }
 
 
 enum GNUNET_GenericReturnValue
-TALER_extensions_is_json_config (
+TALER_extensions_parse_manifest (
   json_t *obj,
   int *critical,
   const char **version,
@@ -265,22 +295,23 @@ TALER_extensions_is_json_config (
 
 
 enum GNUNET_GenericReturnValue
-TALER_extensions_load_json_config (
+TALER_extensions_load_manifests (
   json_t *extensions)
 {
   const char*name;
-  json_t *blob;
+  json_t *manifest;
 
   GNUNET_assert (NULL != extensions);
   GNUNET_assert (json_is_object (extensions));
 
-  json_object_foreach (extensions, name, blob)
+  json_object_foreach (extensions, name, manifest)
   {
     int critical;
     const char *version;
     json_t *config;
-    const struct TALER_Extension *extension =
-      TALER_extensions_get_by_name (name);
+    struct TALER_Extension *extension =  (struct
+                                          TALER_Extension *)
+                                        TALER_extensions_get_by_name (name);
 
     if (NULL == extension)
     {
@@ -291,45 +322,109 @@ TALER_extensions_load_json_config (
 
     /* load and verify criticality, version, etc. */
     if (GNUNET_OK !=
-        TALER_extensions_is_json_config (
-          blob, &critical, &version, &config))
+        TALER_extensions_parse_manifest (
+          manifest, &critical, &version, &config))
       return GNUNET_SYSERR;
 
     if (critical != extension->critical
         || 0 != strcmp (version, extension->version) // TODO: libtool compare?
         || NULL == config
-        || GNUNET_OK != extension->test_json_config (config))
+        || GNUNET_OK != extension->load_config (NULL, config))
       return GNUNET_SYSERR;
 
     /* This _should_ work now */
     if (GNUNET_OK !=
-        extension->load_json_config ((struct TALER_Extension *) extension,
-                                     config))
+        extension->load_config (extension, config))
       return GNUNET_SYSERR;
+
+    extension->enabled = true;
   }
 
   /* make sure to disable all extensions that weren't mentioned in the json */
-  for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+  for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
        NULL != it;
        it = it->next)
   {
-    if (NULL == json_object_get (extensions, it->name))
-      it->disable ((struct TALER_Extension *) it);
+    if (NULL == json_object_get (extensions, it->extension->name))
+      it->extension->disable ((struct TALER_Extension *) it);
   }
 
   return GNUNET_OK;
 }
 
 
-bool
-TALER_extensions_age_restriction_is_enabled ()
+/*
+ * Policy related
+ */
+
+static char *fulfillment2str[] =  {
+  [TALER_PolicyFulfillmentReady]        = "Ready",
+  [TALER_PolicyFulfillmentSuccess]      = "Success",
+  [TALER_PolicyFulfillmentFailure]      = "Failure",
+  [TALER_PolicyFulfillmentTimeout]      = "Timeout",
+  [TALER_PolicyFulfillmentInsufficient] = "Insufficient",
+};
+
+const char *
+TALER_policy_fulfillment_state_str (
+  enum TALER_PolicyFulfillmentState state)
 {
-  const struct TALER_Extension *age =
-    TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+  GNUNET_assert (TALER_PolicyFulfillmentStateCount > state);
+  return fulfillment2str[state];
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_create_policy_details (
+  const json_t *policy_options,
+  struct TALER_PolicyDetails *details,
+  const char **error_hint)
+{
+  enum GNUNET_GenericReturnValue ret;
+  const struct TALER_Extension *extension;
+  const json_t *jtype;
+  const char *type;
+
+  *error_hint = NULL;
+
+  if ((NULL == policy_options) ||
+      (! json_is_object (policy_options)))
+  {
+    *error_hint = "invalid policy object";
+    return GNUNET_SYSERR;
+  }
+
+  jtype = json_object_get (policy_options, "type");
+  if (NULL == jtype)
+  {
+    *error_hint = "no type in policy object";
+    return GNUNET_SYSERR;
+  }
+
+  type = json_string_value (jtype);
+  if (NULL == type)
+  {
+    *error_hint = "invalid type in policy object";
+    return GNUNET_SYSERR;
+  }
+
+  extension = TALER_extensions_get_by_name (type);
+  if ((NULL == extension) ||
+      (NULL == extension->create_policy_details))
+  {
+    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unsupported extension policy '%s' requested\n",
+                type);
+    return GNUNET_NO;
+  }
+
+  details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
+  ret = extension->create_policy_details (policy_options,
+                                          details,
+                                          error_hint);
+  return ret;
 
-  return (NULL != age &&
-          NULL != age->config_json &&
-          TALER_extensions_age_restriction_is_configured ());
 }
 
 
diff --git a/src/include/taler_auditor_service.h 
b/src/include/taler_auditor_service.h
index 30d18e6e..c20b789c 100644
--- a/src/include/taler_auditor_service.h
+++ b/src/include/taler_auditor_service.h
@@ -233,7 +233,7 @@ typedef void
  *
  * @param auditor the auditor handle; the auditor must be ready to operate
  * @param h_wire hash of merchant wire details
- * @param h_extensions hash over the extensions, if any
+ * @param h_policy hash over the policy, if any
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the auditor)
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far in the future
  * @param wire_deadline date until which the exchange should wire the funds
@@ -257,7 +257,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
 TALER_AUDITOR_deposit_confirmation (
   struct TALER_AUDITOR_Handle *auditor,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
diff --git a/src/include/taler_auditordb_plugin.h 
b/src/include/taler_auditordb_plugin.h
index 9f46004c..cf27668b 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -382,9 +382,9 @@ struct TALER_AUDITORDB_DepositConfirmation
   struct TALER_PrivateContractHashP h_contract_terms;
 
   /**
-   * Hash over the extensions for the deposit.
+   * Hash over the policy extension for the deposit.
    */
-  struct TALER_ExtensionContractHashP h_extensions;
+  struct TALER_ExtensionPolicyHashP h_policy;
 
   /**
    * Hash over the wiring information of the merchant.
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 4c478cef..6b800093 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -638,10 +638,9 @@ struct TALER_PrivateContractHashP
 
 
 /**
- * Hash used to represent the "public" extensions to
- * a contract that is shared with the exchange.
+ * Hash used to represent the policy extension to a deposit
  */
-struct TALER_ExtensionContractHashP
+struct TALER_ExtensionPolicyHashP
 {
   /**
    * Actual hash value.
@@ -727,10 +726,10 @@ struct TALER_PickupIdentifierP
 
 
 /**
- * @brief Salted hash over the JSON object representing the configuration of an
- * extension.
+ * @brief Salted hash over the JSON object representing the manifests of
+ * extensions.
  */
-struct TALER_ExtensionConfigHashP
+struct TALER_ExtensionManifestsHashP
 {
   /**
    * Actual hash value.
@@ -3213,7 +3212,7 @@ TALER_wallet_reserve_attest_request_verify (
  * @param h_wire hash of the merchant’s account details
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the exchange)
  * @param h_age_commitment hash over the age commitment, if applicable to the 
denomination (maybe NULL)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
  * @param h_denom_pub hash of the coin denomination's public key
  * @param coin_priv coin’s private key
  * @param wallet_timestamp timestamp when the contract was finalized, must not 
be too far in the future
@@ -3228,7 +3227,7 @@ TALER_wallet_deposit_sign (
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_AgeCommitmentHash *h_age_commitment,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_DenominationHashP *h_denom_pub,
   struct GNUNET_TIME_Timestamp wallet_timestamp,
   const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3245,7 +3244,7 @@ TALER_wallet_deposit_sign (
  * @param h_wire hash of the merchant’s account details
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the exchange)
  * @param h_age_commitment hash over the age commitment (maybe all zeroes, if 
not applicable to the denomination)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
  * @param h_denom_pub hash of the coin denomination's public key
  * @param wallet_timestamp timestamp when the contract was finalized, must not 
be too far in the future
  * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
@@ -3261,7 +3260,7 @@ TALER_wallet_deposit_verify (
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_AgeCommitmentHash *h_age_commitment,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_DenominationHashP *h_denom_pub,
   struct GNUNET_TIME_Timestamp wallet_timestamp,
   const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3666,7 +3665,7 @@ typedef enum TALER_ErrorCode
  * @param scb function to call to create the signature
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the exchange)
  * @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far off
  * @param wire_deadline date until which the exchange should wire the funds
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the exchange (can be zero if refunds are not allowed); must 
not be after the @a wire_deadline
@@ -3682,7 +3681,7 @@ TALER_exchange_online_deposit_confirmation_sign (
   TALER_ExchangeSignCallback scb,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
@@ -3698,7 +3697,7 @@ TALER_exchange_online_deposit_confirmation_sign (
  *
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the exchange)
  * @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far off
  * @param wire_deadline date until which the exchange should wire the funds
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the exchange (can be zero if refunds are not allowed); must 
not be after the @a wire_deadline
@@ -3713,7 +3712,7 @@ enum GNUNET_GenericReturnValue
 TALER_exchange_online_deposit_confirmation_verify (
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
@@ -5257,31 +5256,31 @@ TALER_merchant_contract_sign (
 /* **************** /management/extensions offline signing **************** */
 
 /**
- * Create a signature for the hash of the configuration of an extension
+ * Create a signature for the hash of the manifests of extensions
  *
- * @param h_config hash of the JSON object representing the configuration
+ * @param h_manifests hash of the JSON object representing the manifests
  * @param master_priv private key to sign with
  * @param[out] master_sig where to write the signature
  */
 void
-TALER_exchange_offline_extension_config_hash_sign (
-  const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_sign (
+  const struct TALER_ExtensionManifestsHashP *h_manifests,
   const struct TALER_MasterPrivateKeyP *master_priv,
   struct TALER_MasterSignatureP *master_sig);
 
 
 /**
  * Verify the signature in @a master_sig of the given hash, taken over the JSON
- * blob representing the configuration of an extension
+ * blob representing the manifests of extensions
  *
- * @param h_config hash of the JSON blob of a configuration of an extension
+ * @param h_manifest hash of the JSON blob of manifests of extensions
  * @param master_pub master public key of the exchange
  * @param master_sig signature of the exchange
  * @return #GNUNET_OK if signature is valid
  */
 enum GNUNET_GenericReturnValue
-TALER_exchange_offline_extension_config_hash_verify (
-  const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_verify (
+  const struct TALER_ExtensionManifestsHashP *h_manifest,
   const struct TALER_MasterPublicKeyP *master_pub,
   const struct TALER_MasterSignatureP *master_sig
   );
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index aa9a5b60..e3a349a9 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -894,9 +894,9 @@ struct TALER_EXCHANGE_DepositContractDetail
   struct TALER_PrivateContractHashP h_contract_terms;
 
   /**
-   * Extension-specific details about the deposit relevant to the exchange.
+   * Policy extension specific details about the deposit relevant to the 
exchange.
    */
-  const json_t *extension_details;
+  json_t *policy_details;
 
   /**
    * Timestamp when the contract was finalized, must match approximately the
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 9640e052..f21301e7 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -26,6 +26,7 @@
 #include <gnunet/gnunet_db_lib.h>
 #include "taler_json_lib.h"
 #include "taler_signatures.h"
+#include "taler_extensions_policy.h"
 
 
 /**
@@ -220,7 +221,8 @@ enum TALER_EXCHANGEDB_ReplicatedTable
   TALER_EXCHANGEDB_RT_RECOUP,
   TALER_EXCHANGEDB_RT_RECOUP_REFRESH,
   TALER_EXCHANGEDB_RT_EXTENSIONS,
-  TALER_EXCHANGEDB_RT_EXTENSION_DETAILS,
+  TALER_EXCHANGEDB_RT_POLICY_DETAILS,
+  TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS,
   TALER_EXCHANGEDB_RT_PURSE_REQUESTS,
   TALER_EXCHANGEDB_RT_PURSE_DECISION,
   TALER_EXCHANGEDB_RT_PURSE_MERGES,
@@ -438,8 +440,8 @@ struct TALER_EXCHANGEDB_TableData
       struct TALER_CoinSpendSignatureP coin_sig;
       struct TALER_WireSaltP wire_salt;
       struct TALER_PaytoHashP wire_target_h_payto;
-      bool extension_blocked;
-      uint64_t extension_details_serial_id;
+      bool policy_blocked;
+      uint64_t policy_details_serial_id;
     } deposits;
 
     struct
@@ -510,13 +512,32 @@ struct TALER_EXCHANGEDB_TableData
     struct
     {
       char *name;
-      char *config;
+      char *manifest;
     } extensions;
 
     struct
     {
-      char *extension_options;
-    } extension_details;
+      struct GNUNET_HashCode hash_code;
+      json_t *policy_json;
+      bool no_policy_json;
+      struct GNUNET_TIME_Timestamp deadline;
+      struct TALER_Amount commitment;
+      struct TALER_Amount accumulated_total;
+      struct TALER_Amount fee;
+      struct TALER_Amount transferable;
+      uint16_t fulfillment_state; /* will also be recomputed */
+      uint64_t fulfillment_id;
+      bool no_fulfillment_id;
+    } policy_details;
+
+    struct
+    {
+      struct GNUNET_TIME_Timestamp fulfillment_timestamp;
+      char *fulfillment_proof;
+      struct GNUNET_HashCode h_fulfillment_proof;
+      struct GNUNET_HashCode *policy_hash_codes;
+      size_t policy_hash_codes_count;
+    } policy_fulfillments;
 
     struct
     {
@@ -1511,12 +1532,6 @@ struct TALER_EXCHANGEDB_Deposit
    */
   char *receiver_wire_account;
 
-  /**
-   * Additional details for extensions relevant for this
-   * deposit operation, possibly NULL!
-   */
-  json_t *extension_details;
-
   /**
    * Time when this request was generated.  Used, for example, to
    * assess when (roughly) the income was achieved for tax purposes.
@@ -1558,6 +1573,16 @@ struct TALER_EXCHANGEDB_Deposit
    */
   struct TALER_Amount deposit_fee;
 
+  /*
+   * True if @e policy_json was provided
+   */
+  bool has_policy;
+
+  /**
+   * Hash over the policy data for this deposit (remains unknown to the
+   * Exchange).  Needed for the verification of the deposit's signature
+   */
+  struct TALER_ExtensionPolicyHashP h_policy;
 };
 
 
@@ -1656,6 +1681,17 @@ struct TALER_EXCHANGEDB_DepositListEntry
    */
   struct TALER_Amount deposit_fee;
 
+  /*
+   * True if a policy was provided with the deposit request
+   */
+  bool has_policy;
+
+  /**
+   * Hash over the policy data for this deposit (remains unknown to the
+   * Exchange).  Needed for the verification of the deposit's signature
+   */
+  struct TALER_ExtensionPolicyHashP h_policy;
+
   /**
    * Has the deposit been wired?
    */
@@ -3530,6 +3566,40 @@ struct TALER_EXCHANGEDB_Plugin
     bool *conflict,
     bool *nonce_reuse);
 
+  /**
+   * Retrieve the details to a policy given by its hash_code
+   *
+   * @param cls the `struct PostgresClosure` with the plugin-specific state
+   * @param hc Hash code that identifies the policy
+   * @param[out] detail retrieved policy details
+   * @return query execution status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*get_policy_details)(
+    void *cls,
+    const struct GNUNET_HashCode *hc,
+    struct TALER_PolicyDetails *detail);
+
+  /**
+   * Persist the policy details that extends a deposit.  The particular policy
+   * - referenced by details->hash_code - might already exist in the table, in
+   * which case the call will update the contents of the record with @e details
+   *
+   * @param cls the `struct PostgresClosure` with the plugin-specific state
+   * @param details The parsed `struct TALER_PolicyDetails` according to the 
responsible policy extension.
+   * @param[out] policy_details_serial_id The ID of the entry in the 
policy_details table
+   * @param[out] accumulated_total The total amount accumulated in that policy
+   * @param[out] fulfillment_state The state of policy.  If the state was 
Insufficient prior to the call and the provided deposit raises the 
accumulated_total above the commitment, it will be set to Ready.
+   * @return query execution status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*persist_policy_details)(
+    void *cls,
+    const struct TALER_PolicyDetails *details,
+    uint64_t *policy_details_serial_id,
+    struct TALER_Amount *accumulated_total,
+    enum TALER_PolicyFulfillmentState *fulfillment_state);
+
 
   /**
    * Perform deposit operation, checking for sufficient balance
@@ -3539,7 +3609,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param deposit deposit operation details
    * @param known_coin_id row of the coin in the known_coins table
    * @param h_payto hash of the merchant's payto URI
-   * @param extension_blocked true if an extension is blocking the wire 
transfer
+   * @param policy_details_serial_id (pointer to) the row ID of the policy 
details, maybe NULL
    * @param[in,out] exchange_timestamp time to use for the deposit (possibly 
updated)
    * @param[out] balance_ok set to true if the balance was sufficient
    * @param[out] in_conflict set to true if the deposit conflicted
@@ -3551,7 +3621,7 @@ struct TALER_EXCHANGEDB_Plugin
     const struct TALER_EXCHANGEDB_Deposit *deposit,
     uint64_t known_coin_id,
     const struct TALER_PaytoHashP *h_payto,
-    bool extension_blocked,
+    uint64_t *policy_details_serial_id,
     struct GNUNET_TIME_Timestamp *exchange_timestamp,
     bool *balance_ok,
     bool *in_conflict);
@@ -3580,6 +3650,19 @@ struct TALER_EXCHANGEDB_Plugin
     bool *balance_ok);
 
 
+  /**
+   * Add a proof of fulfillment of an policy
+   *
+   * @param cls the plugin-specific state
+   * @param[in,out] fulfillment The proof of fulfillment and serial_ids of the 
policy_details along with their new state and potential new amounts.
+   * @return query execution status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*add_policy_fulfillment_proof)(
+    void *cls,
+    struct TALER_PolicyFulfillmentTransactionData *fulfillment);
+
+
   /**
    * Check if the given @a nonce was properly locked to the given @a 
old_coin_pub. If so, check if we already
    * created CS signatures for the given @a nonce and @a new_denom_pub_hashes,
@@ -5559,33 +5642,33 @@ struct TALER_EXCHANGEDB_Plugin
 
 
   /**
-   * Function called to save the configuration of an extension
-   * (age-restriction, peer2peer, ...)
+   * Function called to save the manifest of an extension
+   * (age-restriction, policy-extension, ...)
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param extension_name the name of the extension
-   * @param config JSON object of the configuration as string, maybe NULL (== 
disabled extension)
+   * @param manifest JSON object of the Manifest as string, maybe NULL (== 
disabled extension)
    * @return transaction status code
    */
   enum GNUNET_DB_QueryStatus
-  (*set_extension_config)(void *cls,
-                          const char *extension_name,
-                          const char *config);
+  (*set_extension_manifest)(void *cls,
+                            const char *extension_name,
+                            const char *manifest);
 
 
   /**
-   * Function called to retrieve the configuration of an extension
-   * (age-restriction, peer2peer, ...)
+   * Function called to retrieve the manifest of an extension
+   * (age-restriction, policy-extension, ...)
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param extension_name the name of the extension
-   * @param[out] config JSON object of the configuration as string, maybe NULL 
(== disabled extension)
+   * @param[out] manifest Manifest of the extension in JSON encoding, maybe 
NULL (== disabled extension)
    * @return transaction status code
    */
   enum GNUNET_DB_QueryStatus
-  (*get_extension_config)(void *cls,
-                          const char *extension_name,
-                          char **config);
+  (*get_extension_manifest)(void *cls,
+                            const char *extension_name,
+                            char **manifest);
 
 
   /**
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 8e1823cc..5e53d27f 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2014-2021 Taler Systems SA
+   Copyright (C) 2022 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
@@ -24,48 +24,195 @@
 #include <gnunet/gnunet_util_lib.h>
 #include "taler_crypto_lib.h"
 #include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_extensions_policy.h"
 
 
 #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
 
 enum TALER_Extension_Type
 {
-  TALER_Extension_AgeRestriction = 0,
-  TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
+  TALER_Extension_PolicyNull                 = 0,
+
+  TALER_Extension_AgeRestriction             = 1,
+  TALER_Extension_PolicyMerchantRefund       = 2,
+  TALER_Extension_PolicyBrandtVickeryAuction = 3,
+  TALER_Extension_PolicyEscrowedPayment      = 4,
+
+  TALER_Extension_MaxPredefined              = 5 // Must be last of the 
predefined
 };
 
+
+/* Forward declarations */
+enum TALER_PolicyFulfillmentState;
+struct TALER_PolicyFulfillmentOutcome;
+
 /*
  * @brief Represents the implementation of an extension.
  *
- * TODO: add documentation
+ * An "Extension" is an optional feature for the Exchange.
+ * There are only two types of extensions:
+ *
+ * a) Age restriction:  This is a special feature that directly interacts with
+ * denominations and coins, but is not define policies during deposits, see b).
+ * The implementation of this extension doesn't have to implement any of the
+ * http- or depost-handlers in the struct.
+ *
+ * b) Policies for deposits:  These are extensions that define policies (such
+ * as refund, escrow or auctions) for deposit requests.  These extensions have
+ * to implement at least the deposit- and post-http-handler in the struct to be
+ * functional.
+ *
+ * In addition to the handlers defined in this struct, an extension must also
+ * be a plugin in the GNUNET_Plugin sense.  That is, it must implement the
+ * functions
+ *    1: (void *ext)libtaler_extension_<name>_init(void *cfg)
+ * and
+ *    2: (void *)libtaler_extension_<name>_done(void *)
+ *
+ * In 1:, the input will be the GNUNET_CONFIGURATION_Handle to the TALER
+ * configuration and the output must be the struct TALER_Extension * on
+ * success, NULL otherwise.
+ *
+ * In 2:, no arguments are passed and NULL is expected to be returned.
  */
 struct TALER_Extension
 {
-  /* simple linked list */
-  struct TALER_Extension *next;
-
+  /**
+   * Type of the extension.  Only one extension of a type can be loaded
+   * at any time.
+   */
   enum TALER_Extension_Type type;
+
+  /**
+   * The name of the extension, must be unique among all loaded extensions.  It
+   * is used in URLs for /extension/$NAME as well.
+   */
   char *name;
+
+  /**
+   * Criticality of the extension.  It has the same semantics as "critical" has
+   * for extensions in X.509:
+   * - if "true", the client must "understand" the extension before proceeding,
+   * - if "false", clients can safely skip extensions they do not understand.
+   * (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)
+   */
   bool critical;
+
+  /**
+   * Version of the extension must be provided in Taler's protocol verison 
ranges notation, see
+   * https://docs.taler.net/core/api-common.html#protocol-version-ranges
+   */
   char *version;
+
+  /**
+   * If the extension is marked as enabled, it will be listed in the
+   * "extensions" field in the "/keys" response.
+   */
+  bool enabled;
+
+  /**
+   * Opaque (public) configuration object, set by the extension.
+   */
   void *config;
-  json_t *config_json;
 
-  void (*disable)(struct TALER_Extension *ext);
 
-  enum GNUNET_GenericReturnValue (*test_json_config)(
-    const json_t *config);
+  /**
+   * @brief Handler to to disable the extension.
+   *
+   * @param ext The current extension object
+   */
+  void (*disable)(struct TALER_Extension *ext);
 
-  enum GNUNET_GenericReturnValue (*load_json_config)(
+  /**
+   * @brief Handler to read an extension-specific configuration in JSON
+   * encoding and enable the extension.  Must be implemented by the extension.
+   *
+   * @param ext The extension object. If NULL, the configuration will only be 
checked.
+   * @param config A JSON blob
+   * @return GNUNET_OK if the json was a valid configuration for the extension.
+   */
+  enum GNUNET_GenericReturnValue (*load_config)(
     struct TALER_Extension *ext,
     json_t *config);
 
-  json_t *(*config_to_json)(
+  /**
+   * @brief Handler to return the manifest of the extension in JSON encoding.
+   *
+   * See
+   * 
https://docs.taler.net/design-documents/006-extensions.html#tsref-type-Extension
+   * for the definition.
+   *
+   * @param ext The extension object
+   * @return The JSON encoding of the extension, if enabled, NULL otherwise.
+   */
+  json_t *(*manifest)(
     const struct TALER_Extension *ext);
 
-  enum GNUNET_GenericReturnValue (*load_taler_config)(
-    struct TALER_Extension *ext,
-    const struct GNUNET_CONFIGURATION_Handle *cfg);
+  /* =========================
+   *  Policy related handlers
+   * =========================
+   */
+
+  /**
+   * @brief Handler to check an incoming policy and create a
+   * TALER_PolicyDetails. Can be NULL;
+   *
+   * When a deposit request refers to this extension in its policy
+   * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler
+   * will be called before the deposit transaction.
+   *
+   * @param[in]  policy_json Details about the policy, provided by the client
+   *             during a deposit request.
+   * @param[out] details On success, will contain the details to the policy,
+   *             evaluated by the corresponding policy handler.
+   * @param[out] error_hint On error, will contain a hint
+   * @return     GNUNET_OK if the data was accepted by the extension.
+   */
+  enum GNUNET_GenericReturnValue (*create_policy_details)(
+    const json_t *policy_json,
+    struct TALER_PolicyDetails *details,
+    const char **error_hint);
+
+  /**
+   * @brief Handler for POST-requests to the /extensions/$name endpoint. Can 
be NULL.
+   *
+   * @param[in] root The JSON body from the request
+   * @param[in] args Additional query parameters of the request.
+   * @param[in,out] details List of policy details related to the incoming 
fulfillment proof
+   * @param[in] details_len Size of the list @e details
+   * @param[out] output JSON output to return to the client
+   * @return GNUNET_OK on success.
+   */
+  enum GNUNET_GenericReturnValue (*policy_post_handler)(
+    const json_t *root,
+    const char *const args[],
+    struct TALER_PolicyDetails *details,
+    size_t details_len,
+    json_t **output);
+
+  /**
+   * @brief Handler for GET-requests to the /extensions/$name endpoint.  Can 
be NULL.
+   *
+   * @param connection The current connection
+   * @param root The JSON body from the request
+   * @param args Additional query parameters of the request.
+   * @return MDH result
+   */
+  MHD_RESULT (*policy_get_handler)(
+    struct MHD_Connection *connection,
+    const char *const args[]);
+};
+
+
+/*
+ * @brief simply linked list of extensions
+ */
+
+struct TALER_Extensions
+{
+  struct TALER_Extensions *next;
+  const struct TALER_Extension *extension;
 };
 
 /**
@@ -73,70 +220,57 @@ struct TALER_Extension
  */
 
 /*
- * @brief Sets the configuration of the extensions from the given TALER
- * configuration.
+ * @brief Loads the extensions as shared libraries, as specified in the given
+ * TALER configuration.
  *
  * @param cfg Handle to the TALER configuration
  * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
  *         or any particular configuration couldn't be parsed.
  */
 enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_init (
   const struct GNUNET_CONFIGURATION_Handle *cfg);
 
 /*
- * @brief Checks the given obj to be a valid extension object and fill the
- * fields accordingly.
+ * @brief Parses a given JSON object as an extension manifest.
  *
- * @param[in] obj Object to verify is a valid extension
+ * @param[in] obj JSON object to parse as an extension manifest
  * @param{out] critical will be set to 1 if the extension is critical 
according to obj
  * @param[out] version will be set to the version of the extension according 
to obj
  * @param[out] config will be set to the configuration of the extension 
according to obj
  * @return OK on success, Error otherwise
  */
 enum GNUNET_GenericReturnValue
-TALER_extensions_is_json_config (
+TALER_extensions_parse_manifest (
   json_t *obj,
   int *critical,
   const char **version,
   json_t **config);
 
 /*
- * @brief Sets the configuration of the extensions from a given JSON object.
+ * @brief Loads extensions according to the manifests.
  *
- * The JSON object must be of type ExchangeKeysResponse as described in
- * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ * The JSON object must be of type ExtensionsManifestsResponse as described
+ * in https://docs.taler.net/design-documents/006-extensions.html#exchange
  *
- * @param cfg JSON object containing the configuration for all extensions
+ * @param cfg JSON object containing the manifests for all extensions
  * @return #GNUNET_OK on success, #GNUNET_SYSERR if unknown extensions were
  *  found or any particular configuration couldn't be parsed.
  */
 enum GNUNET_GenericReturnValue
-TALER_extensions_load_json_config (
-  json_t *cfg);
+TALER_extensions_load_manifests (
+  json_t *manifests);
 
 /*
  * @brief Returns the head of the linked list of extensions.
  */
-const struct TALER_Extension *
+const struct TALER_Extensions *
 TALER_extensions_get_head ();
 
-/*
- * @brief Adds an extension to the linked list of extensions.
- *
- * @param new_extension the new extension to be added
- * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
- * (missing fields), GNUNET_NO if there is already an extension with that name
- * or type.
- */
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
-  struct TALER_Extension *new_extension);
-
 /**
  * @brief Finds and returns a supported extension by a given type.
  *
- * @param type type of the extension to lookup
+ * @param type of the extension to lookup
  * @return extension found, or NULL (should not happen!)
  */
 const struct TALER_Extension *
@@ -154,8 +288,6 @@ const struct TALER_Extension *
 TALER_extensions_get_by_name (
   const char *name);
 
-#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
-
 /**
  * @brief Check if a given type of an extension is enabled
  *
@@ -166,12 +298,21 @@ bool
 TALER_extensions_is_enabled_type (
   enum TALER_Extension_Type type);
 
+/**
+ * @brief Check if an extension is enabled
+ *
+ * @param extension The extension handler.
+ * @return true enabled, false if not enabled, will assert if type is not 
found.
+ */
+bool
+TALER_extensions_is_enabled (
+  const struct TALER_Extension *extension);
 
 /*
  * Verify the signature of a given JSON object for extensions with the master
  * key of the exchange.
  *
- * The JSON object must be of type ExchangeKeysResponse as described in
+ * The JSON object must be of type ExtensionsManifestsResponse as described in
  * https://docs.taler.net/design-documents/006-extensions.html#exchange
  *
  * @param extensions JSON object with the extension configuration
@@ -181,14 +322,19 @@ TALER_extensions_is_enabled_type (
  * and GNUNET_NO if the signature couldn't be verified.
  */
 enum GNUNET_GenericReturnValue
-TALER_extensions_verify_json_config_signature (
-  json_t *extensions,
+TALER_extensions_verify_manifests_signature (
+  json_t *manifests,
   struct TALER_MasterSignatureP *extensions_sig,
   struct TALER_MasterPublicKeyP *master_pub);
 
 
 /*
  * TALER Age Restriction Extension
+ *
+ * This extension is special insofar as it directly interacts with coins and
+ * denominations.
+ *
+ * At the same time, it doesn't implement and http- or deposit-handlers.
  */
 
 #define TALER_EXTENSION_SECTION_AGE_RESTRICTION 
(TALER_EXTENSION_SECTION_PREFIX  \
@@ -204,102 +350,39 @@ TALER_extensions_verify_json_config_signature (
                                                           | 1 << 21)
 #define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS 
"8:10:12:14:16:18:21"
 
-/**
- * @brief Registers the extension for age restriction to the list extensions
- */
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ();
 
-/**
- * @brief Parses a string as a list of age groups.
- *
- * The string must consist of a colon-separated list of increasing integers
- * between 0 and 31.  Each entry represents the beginning of a new age group.
- * F.e. the string
- *
- *  "8:10:12:14:16:18:21"
- *
- * represents the following list of eight age groups:
- *
- * | Group |    Ages       |
- * | -----:|:------------- |
- * |    0  |  0, 1, ..., 7 |
- * |    1  |  8, 9         |
- * |    2  | 10, 11        |
- * |    3  | 12, 13        |
- * |    4  | 14, 15        |
- * |    5  | 16, 17        |
- * |    6  | 18, 19, 20    |
- * |    7  | 21, ...       |
- *
- * which is then encoded as a bit mask with the corresponding bits set:
- *
- *  31     24        16        8         0
- *  |      |         |         |         |
- *  oooooooo  oo1oo1o1  o1o1o1o1  ooooooo1
- *
- * @param groups String representation of age groups
- * @param[out] mask Mask representation for age restriction.
- * @return Error, if age groups were invalid, OK otherwise.
+/*
+ * @brief Configuration for Age Restriction
  */
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
-  const char *groups,
-  struct TALER_AgeMask *mask);
+struct TALER_AgeRestrictionConfig
+{
+  struct TALER_AgeMask mask;
+  uint8_t num_groups;
+};
 
-/**
- * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
- *
- * @param mask Age mask
- * @return String representation of the age mask, allocated by GNUNET_malloc.
- *         Can be used as value in the TALER config.
- */
-char *
-TALER_age_mask_to_string (
-  const struct TALER_AgeMask *mask);
 
 /**
- * @brief Returns true when age restriction is configured and enabled.
+ * @brief Retrieve the age restriction configuration
+ *
+ * @return age restriction configuration if present, otherwise NULL.
  */
-bool
-TALER_extensions_age_restriction_is_enabled ();
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ();
 
 /**
- * @brief Returns true when age restriction is configured (might not be
- * _enabled_, though).
+ * @brief Check if age restriction is enabled
+ *
+ * @return true, if age restriction is loaded, configured and enabled; 
otherwise false.
  */
 bool
-TALER_extensions_age_restriction_is_configured ();
-
-/**
- * @brief Returns the currently set age mask.  Note that even if age
- * restriction is not enabled, the age mask might be have a non-zero value.
- */
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ();
-
+TALER_extensions_is_age_restriction_enabled ();
 
 /**
- * @brief Returns the amount of age groups defined.  0 means no age restriction
- * enabled.
- */
-size_t
-TALER_extensions_age_restriction_num_groups ();
-
-/**
- * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ * @brief Return the age mask for age restriction
  *
- * @param root is the json object
- * @param[out] mask on success, will contain the age mask
- * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
- */
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
-                             struct TALER_AgeMask *mask);
-
-
-/*
- * TODO: Add Peer2Peer Extension
+ * @return configured age mask, if age restriction is loaded, configured and 
enabled; otherwise zero mask.
  */
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ();
 
 #endif
diff --git a/src/include/taler_extensions_policy.h 
b/src/include/taler_extensions_policy.h
new file mode 100644
index 00000000..14a581f3
--- /dev/null
+++ b/src/include/taler_extensions_policy.h
@@ -0,0 +1,198 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 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 include/taler_extensions_policy.h
+ * @brief Interface for policy extensions
+ * @author Özgür Kesim
+ */
+#ifndef TALER_EXTENSIONS_POLICY_H
+#define TALER_EXTENSIONS_POLICY_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_crypto_lib.h"
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+
+/*
+ * @brief Describes the states of fulfillment of a policy bound to a deposit
+ */
+enum TALER_PolicyFulfillmentState
+{
+  /* General error state of an fulfillment. */
+  TALER_PolicyFulfillmentFailure = 0,
+
+  /* The policy is not yet ready due to insufficient funding. More deposits are
+   * necessary for it to become ready . */
+  TALER_PolicyFulfillmentInsufficient = 1,
+
+  /* The policy is funded and ready, pending */
+  TALER_PolicyFulfillmentReady = 2,
+
+  /* Policy is provably fulfilled. */
+  TALER_PolicyFulfillmentSuccess = 3,
+
+  /* Policy fulfillment has timed out */
+  TALER_PolicyFulfillmentTimeout = 4,
+
+  TALER_PolicyFulfillmentStateCount = TALER_PolicyFulfillmentTimeout + 1
+};
+
+
+/*
+ * @brief Returns a string representation of the state of a policy fulfillment
+ */
+const char *
+TALER_policy_fulfillment_state_str (enum TALER_PolicyFulfillmentState state);
+
+
+/* @brief Details of a policy for a deposit request */
+struct TALER_PolicyDetails
+{
+  /* Hash code that should be used for the .policy_hash_code field when
+   * this policy is saved in the policy_details table. */
+  struct GNUNET_HashCode hash_code;
+
+  /* Content of the policy in its original JSON form */
+  json_t *policy_json;
+
+  /* When the deadline is meat and the policy is still in "Ready" state,
+   * a timeout-handler will transfer the amount
+   *    (total_amount - policy_fee - refreshable_amount)
+   * to the payto-URI from the corresponding deposit.  The value
+   * amount_refreshable will be refreshable by the owner of the
+   * associated deposits's coins */
+  struct GNUNET_TIME_Timestamp deadline;
+
+  /* The amount to which this policy commits to. It must be at least as
+   * large as @e policy_fee. */
+  struct TALER_Amount commitment;
+
+  /* The total sum of contributions from coins so far to fund this
+   * policy.  It must be at least as large as @commitment in order to be
+   * sufficiently funded. */
+  struct TALER_Amount accumulated_total;
+
+  /* The fee from the exchange for handling the policy. It is due when
+   * the state changes to Timeout or Success. */
+  struct TALER_Amount policy_fee;
+
+  /* The amount that will be transfered to the payto-URIs from the
+   * corresponding deposits when the fulfillment state changes to Timeout
+   * or Success.  Note that a fulfillment handler can alter this upon
+   * arrival of a proof of fulfillment. The remaining amount
+   * (accumulated_amount - policy_amount - transferable_amount) */
+  struct TALER_Amount transferable_amount;
+
+  /* The state of fulfillment of a policy.
+   * - If the state is Insufficient, the client is required to call
+   *   /deposit -maybe multiple times- with enough coins and the same
+   *   policy details in order to reach the required amount. The state is
+   *   then changed to Ready.
+   * - If the state changes to Timeout or Success, a handler will transfer
+   *   the amount (total_amount - policy_fee - refreshable_amount) to the
+   *   payto-URI from the corresponding deposit.  The value
+   *   amount_refreshable will be refreshable by the owner of the
+   *   associated deposits's coins.  */
+  enum TALER_PolicyFulfillmentState fulfillment_state;
+
+  /* If there is a proof of fulfillment, the row ID from the
+   * policy_fulfillment table */
+  uint64_t policy_fulfillment_id;
+  bool no_policy_fulfillment_id;
+};
+
+/*
+ * @brief All information required for the database transaction when handling a
+ * proof of fulfillment request.
+ */
+struct TALER_PolicyFulfillmentTransactionData
+{
+  /* The incoming proof, provided by a client */
+  const json_t *proof;
+
+  /* The Hash of the proof */
+  struct GNUNET_HashCode h_proof;
+
+  /* The timestamp of retrieval of the proof */
+  struct GNUNET_TIME_Timestamp timestamp;
+
+  /* The ID of the proof in the policy_fulfillment table.  Will be set
+   * during the transaction.  Needed to fill the table
+   * policy_details_fulfillments. */
+  uint64_t fulfillment_id;
+
+  /* The list of policy details.  Will be updated by the policy handler */
+  struct TALER_PolicyDetails *details;
+  size_t details_count;
+};
+
+
+/*
+ * @brief Extracts policy details from the deposit's policy options and the 
policy extensions
+ *
+ * @param[in]  policy_options JSON of the policy options from a deposit request
+ * @param[out] details On GNUNET_OK, the parsed details
+ * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason 
why it failed
+ * @return GNUNET_OK on success, GNUNET_NO, when no extension was found. 
GNUNET_SYSERR when the JSON was
+ * invalid, with *error_hint maybe non-NULL.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_create_policy_details (
+  const json_t *policy_options,
+  struct TALER_PolicyDetails *details,
+  const char **error_hint);
+
+
+/*
+ * ================================
+ * Merchant refund policy
+ * ================================
+ */
+struct TALER_ExtensionPolicyMerchantRefundPolicyConfig
+{
+  struct GNUNET_TIME_Relative max_timeout;
+};
+
+/*
+ * ================================
+ * Brandt-Vickrey Auctions policy
+ * ================================
+ */
+/*
+ * @brief Configuration for Brandt-Vickrey auctions policy
+ */
+struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig
+{
+  uint16_t max_bidders;
+  uint16_t max_prices;
+  struct TALER_Amount auction_fee;
+};
+
+
+/*
+ * ================================
+ * Escrowed Payments policy
+ * ================================
+ */
+/*
+ * @brief Configuration for escrowed payments policy
+ */
+struct TALER_ExtensionPolicyEscrowedPaymentsConfig
+{
+  struct GNUNET_TIME_Relative max_timeout;
+};
+
+#endif
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 12526e95..d0527cc7 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -765,25 +765,25 @@ TALER_JSON_wire_to_payto (const json_t *wire_s);
 
 
 /**
- * Hash @a extensions in deposits.
+ * Hash @a policy extensions in deposits.
  *
- * @param extensions contract extensions to hash
- * @param[out] ech where to write the extension hash
+ * @param policy contract policy extension to hash
+ * @param[out] ech where to write the policy hash
  */
 void
-TALER_deposit_extension_hash (const json_t *extensions,
-                              struct TALER_ExtensionContractHashP *ech);
+TALER_deposit_policy_hash (const json_t *extensions,
+                           struct TALER_ExtensionPolicyHashP *ech);
 
 /**
- * Hash the @a config of an extension, given as JSON
+ * Hash the @a manifests of extensions, given as JSON
  *
- * @param config configuration of the extension
- * @param[out] eh where to write the extension hash
+ * @param manifests Manifests of the extensions
+ * @param[out] eh where to write the hash
  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 enum GNUNET_GenericReturnValue
-TALER_JSON_extensions_config_hash (const json_t *config,
-                                   struct TALER_ExtensionConfigHashP *eh);
+TALER_JSON_extensions_manifests_hash (const json_t *manifests,
+                                      struct TALER_ExtensionManifestsHashP 
*eh);
 
 /**
  * Canonicalize a JSON input to a string according to RFC 8785.
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 079f72ed..1580f3dc 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -492,5 +492,63 @@ char *strchrnul (const char *s, int c);
 
 #endif
 
+/**
+ * @brief Parses a string as a list of age groups.
+ *
+ * The string must consist of a colon-separated list of increasing integers
+ * between 0 and 31.  Each entry represents the beginning of a new age group.
+ * F.e. the string
+ *
+ *  "8:10:12:14:16:18:21"
+ *
+ * represents the following list of eight age groups:
+ *
+ * | Group |    Ages       |
+ * | -----:|:------------- |
+ * |    0  |  0, 1, ..., 7 |
+ * |    1  |  8, 9         |
+ * |    2  | 10, 11        |
+ * |    3  | 12, 13        |
+ * |    4  | 14, 15        |
+ * |    5  | 16, 17        |
+ * |    6  | 18, 19, 20    |
+ * |    7  | 21, ...       |
+ *
+ * which is then encoded as a bit mask with the corresponding bits set:
+ *
+ *  31     24        16        8         0
+ *  |      |         |         |         |
+ *  oooooooo  oo1oo1o1  o1o1o1o1  ooooooo1
+ *
+ * @param groups String representation of age groups
+ * @param[out] mask Mask representation for age restriction.
+ * @return Error, if age groups were invalid, OK otherwise.
+ */
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+  const char *groups,
+  struct TALER_AgeMask *mask);
+
+/**
+ * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
+ *
+ * @param mask Age mask
+ * @return String representation of the age mask, allocated by GNUNET_malloc.
+ *         Can be used as value in the TALER config.
+ */
+char *
+TALER_age_mask_to_string (
+  const struct TALER_AgeMask *mask);
+
+/**
+ * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ *
+ * @param root is the json object
+ * @param[out] mask on success, will contain the age mask
+ * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
+ */
+enum GNUNET_GenericReturnValue
+TALER_JSON_parse_age_groups (const json_t *root,
+                             struct TALER_AgeMask *mask);
 
 #endif
diff --git a/src/json/json.c b/src/json/json.c
index d4ac3748..7d7e4ecb 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -1008,12 +1008,12 @@ TALER_JSON_get_error_code2 (const void *data,
 
 
 void
-TALER_deposit_extension_hash (const json_t *extensions,
-                              struct TALER_ExtensionContractHashP *ech)
+TALER_deposit_policy_hash (const json_t *policy,
+                           struct TALER_ExtensionPolicyHashP *ech)
 {
   GNUNET_assert (GNUNET_OK ==
-                 dump_and_hash (extensions,
-                                "taler-contract-extensions",
+                 dump_and_hash (policy,
+                                "taler-extensions-policy",
                                 &ech->hash));
 }
 
@@ -1037,11 +1037,11 @@ TALER_JSON_canonicalize (const json_t *input)
 
 
 enum GNUNET_GenericReturnValue
-TALER_JSON_extensions_config_hash (const json_t *config,
-                                   struct TALER_ExtensionConfigHashP *ech)
+TALER_JSON_extensions_manifests_hash (const json_t *manifests,
+                                      struct TALER_ExtensionManifestsHashP 
*ech)
 {
-  return dump_and_hash (config,
-                        "taler-extension-configuration",
+  return dump_and_hash (manifests,
+                        "taler-extensions-manifests",
                         &ech->hash);
 }
 
diff --git a/src/lib/auditor_api_deposit_confirmation.c 
b/src/lib/auditor_api_deposit_confirmation.c
index c4542d0e..82537e25 100644
--- a/src/lib/auditor_api_deposit_confirmation.c
+++ b/src/lib/auditor_api_deposit_confirmation.c
@@ -153,7 +153,7 @@ handle_deposit_confirmation_finished (void *cls,
  * Verify signature information about the deposit-confirmation.
  *
  * @param h_wire hash of merchant wire details
- * @param h_extensions hash over the extensions, if any
+ * @param h_policy hash over the policy extension, if any
  * @param h_contract_terms hash of the contact of the merchant with the 
customer (further details are never disclosed to the auditor)
  * @param exchange_timestamp timestamp when the deposit was received by the 
wallet
  * @param wire_deadline by what time must the amount be wired to the merchant
@@ -172,7 +172,7 @@ handle_deposit_confirmation_finished (void *cls,
  */
 static enum GNUNET_GenericReturnValue
 verify_signatures (const struct TALER_MerchantWireHashP *h_wire,
-                   const struct TALER_ExtensionContractHashP *h_extensions,
+                   const struct TALER_ExtensionPolicyHashP *h_policy,
                    const struct TALER_PrivateContractHashP *h_contract_terms,
                    struct GNUNET_TIME_Timestamp exchange_timestamp,
                    struct GNUNET_TIME_Timestamp wire_deadline,
@@ -192,7 +192,7 @@ verify_signatures (const struct TALER_MerchantWireHashP 
*h_wire,
       TALER_exchange_online_deposit_confirmation_verify (
         h_contract_terms,
         h_wire,
-        h_extensions,
+        h_policy,
         exchange_timestamp,
         wire_deadline,
         refund_deadline,
@@ -239,7 +239,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
 TALER_AUDITOR_deposit_confirmation (
   struct TALER_AUDITOR_Handle *auditor,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
@@ -266,7 +266,7 @@ TALER_AUDITOR_deposit_confirmation (
                  TALER_AUDITOR_handle_is_ready_ (auditor));
   if (GNUNET_OK !=
       verify_signatures (h_wire,
-                         h_extensions,
+                         h_policy,
                          h_contract_terms,
                          exchange_timestamp,
                          wire_deadline,
@@ -290,8 +290,8 @@ TALER_AUDITOR_deposit_confirmation (
     = GNUNET_JSON_PACK (
         GNUNET_JSON_pack_data_auto ("h_wire",
                                     h_wire),
-        GNUNET_JSON_pack_data_auto ("h_extensions",
-                                    h_extensions),
+        GNUNET_JSON_pack_data_auto ("h_policy",
+                                    h_policy),
         GNUNET_JSON_pack_data_auto ("h_contract_terms",
                                     h_contract_terms),
         GNUNET_JSON_pack_timestamp ("exchange_timestamp",
diff --git a/src/lib/exchange_api_batch_deposit.c 
b/src/lib/exchange_api_batch_deposit.c
index 39c1c9b4..ca5c3c61 100644
--- a/src/lib/exchange_api_batch_deposit.c
+++ b/src/lib/exchange_api_batch_deposit.c
@@ -99,7 +99,7 @@ struct TALER_EXCHANGE_BatchDepositHandle
   /**
    * Hash over the extensions, or all zero.
    */
-  struct TALER_ExtensionContractHashP h_extensions;
+  struct TALER_ExtensionPolicyHashP h_policy;
 
   /**
    * Time when this confirmation was generated / when the exchange received
@@ -185,7 +185,7 @@ auditor_cb (void *cls,
   aie->dch = TALER_AUDITOR_deposit_confirmation (
     ah,
     &dh->h_wire,
-    &dh->h_extensions,
+    &dh->h_policy,
     &dh->dcd.h_contract_terms,
     dh->exchange_timestamp,
     dh->dcd.wire_deadline,
@@ -317,7 +317,7 @@ handle_deposit_finished (void *cls,
             TALER_exchange_online_deposit_confirmation_verify (
               &dh->dcd.h_contract_terms,
               &dh->h_wire,
-              &dh->h_extensions,
+              &dh->h_policy,
               dh->exchange_timestamp,
               dh->dcd.wire_deadline,
               dh->dcd.refund_deadline,
@@ -492,9 +492,9 @@ TALER_EXCHANGE_batch_deposit (
                             * sizeof (*cdds));
   dh->num_cdds = num_cdds;
   dh->dcd = *dcd;
-  if (NULL != dcd->extension_details)
-    TALER_deposit_extension_hash (dcd->extension_details,
-                                  &dh->h_extensions);
+  if (NULL != dcd->policy_details)
+    TALER_deposit_policy_hash (dcd->policy_details,
+                               &dh->h_policy);
   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
                                       &dcd->wire_salt,
                                       &dh->h_wire);
@@ -533,7 +533,7 @@ TALER_EXCHANGE_batch_deposit (
 
     if (GNUNET_OK !=
         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
-                                                  &dh->h_extensions,
+                                                  &dh->h_policy,
                                                   &dh->h_wire,
                                                   cdd,
                                                   dki))
@@ -586,8 +586,8 @@ TALER_EXCHANGE_batch_deposit (
     GNUNET_JSON_pack_array_steal ("coins",
                                   deposits),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_object_steal ("extension_details",
-                                     NULL)), /* FIXME #7270-Oec */
+      GNUNET_JSON_pack_object_steal ("policy_details",
+                                     dcd->policy_details)),
     GNUNET_JSON_pack_timestamp ("timestamp",
                                 dcd->timestamp),
     GNUNET_JSON_pack_data_auto ("merchant_pub",
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index 3041e6e0..3807b997 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -844,7 +844,8 @@ help_deposit (struct CoinHistoryParseContext *pc,
 {
   struct TALER_MerchantWireHashP h_wire;
   struct TALER_PrivateContractHashP h_contract_terms;
-  // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
+  struct TALER_ExtensionPolicyHashP h_policy;
+  bool no_h_policy;
   struct GNUNET_TIME_Timestamp wallet_timestamp;
   struct TALER_MerchantPublicKeyP merchant_pub;
   struct GNUNET_TIME_Timestamp refund_deadline = {0};
@@ -863,6 +864,10 @@ help_deposit (struct CoinHistoryParseContext *pc,
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
                                    &hac),
       &no_hac),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_fixed_auto ("h_policy",
+                                   &h_policy),
+      &no_h_policy),
     GNUNET_JSON_spec_timestamp ("timestamp",
                                 &wallet_timestamp),
     GNUNET_JSON_spec_mark_optional (
@@ -891,7 +896,7 @@ help_deposit (struct CoinHistoryParseContext *pc,
         &h_wire,
         &h_contract_terms,
         no_hac ? NULL : &hac,
-        NULL /* h_extensions! */,
+        no_h_policy ? NULL : &h_policy,
         &pc->dk->h_key,
         wallet_timestamp,
         &merchant_pub,
@@ -2143,7 +2148,7 @@ TALER_EXCHANGE_get_min_denomination_ (
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_verify_deposit_signature_ (
   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
-  const struct TALER_ExtensionContractHashP *ech,
+  const struct TALER_ExtensionPolicyHashP *ech,
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
   const struct TALER_EXCHANGE_DenomPublicKey *dki)
diff --git a/src/lib/exchange_api_common.h b/src/lib/exchange_api_common.h
index a75ed3ed..80c36daf 100644
--- a/src/lib/exchange_api_common.h
+++ b/src/lib/exchange_api_common.h
@@ -203,7 +203,7 @@ TALER_EXCHANGE_get_min_denomination_ (
  * Verify signature information about the deposit.
  *
  * @param dcd contract details
- * @param ech hashed contract (passed to avoid recomputation)
+ * @param ech hashed policy (passed to avoid recomputation)
  * @param h_wire hashed wire details (passed to avoid recomputation)
  * @param cdd coin-specific details
  * @param dki denomination of the coin
@@ -212,7 +212,7 @@ TALER_EXCHANGE_get_min_denomination_ (
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_verify_deposit_signature_ (
   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
-  const struct TALER_ExtensionContractHashP *ech,
+  const struct TALER_ExtensionPolicyHashP *ech,
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
   const struct TALER_EXCHANGE_DenomPublicKey *dki);
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 3ba986b2..8f179b72 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -97,9 +97,9 @@ struct TALER_EXCHANGE_DepositHandle
   struct TALER_MerchantWireHashP h_wire;
 
   /**
-   * Hash over the extensions, or all zero.
+   * Hash over the policy extension, or all zero.
    */
-  struct TALER_ExtensionContractHashP h_extensions;
+  struct TALER_ExtensionPolicyHashP h_policy;
 
   /**
    * Time when this confirmation was generated / when the exchange received
@@ -177,7 +177,7 @@ auditor_cb (void *cls,
   aie->dch = TALER_AUDITOR_deposit_confirmation (
     ah,
     &dh->h_wire,
-    &dh->h_extensions,
+    &dh->h_policy,
     &dh->dcd.h_contract_terms,
     dh->exchange_timestamp,
     dh->dcd.wire_deadline,
@@ -277,7 +277,7 @@ handle_deposit_finished (void *cls,
           TALER_exchange_online_deposit_confirmation_verify (
             &dh->dcd.h_contract_terms,
             &dh->h_wire,
-            &dh->h_extensions,
+            &dh->h_policy,
             dh->exchange_timestamp,
             dh->dcd.wire_deadline,
             dh->dcd.refund_deadline,
@@ -446,15 +446,15 @@ TALER_EXCHANGE_deposit (
   dh->cb_cls = cb_cls;
   dh->cdd = *cdd;
   dh->dcd = *dcd;
-  if (NULL != dcd->extension_details)
-    TALER_deposit_extension_hash (dcd->extension_details,
-                                  &dh->h_extensions);
+  if (NULL != dcd->policy_details)
+    TALER_deposit_policy_hash (dcd->policy_details,
+                               &dh->h_policy);
   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
                                       &dcd->wire_salt,
                                       &dh->h_wire);
   if (GNUNET_OK !=
       TALER_EXCHANGE_verify_deposit_signature_ (dcd,
-                                                &dh->h_extensions,
+                                                &dh->h_policy,
                                                 &dh->h_wire,
                                                 cdd,
                                                 dki))
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index d6e12cc1..14ba7317 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -898,17 +898,20 @@ decode_keys_json (const json_t *resp_obj,
   /* TODO: maybe lift all this into a FP in TALER_Extension ? */
   {
     struct TALER_MasterSignatureP extensions_sig = {0};
-    json_t *extensions = NULL;
+    json_t *manifests = NULL;
+    bool no_extensions = false;
+    bool no_signature = false;
+
     struct GNUNET_JSON_Specification ext_spec[] = {
       GNUNET_JSON_spec_mark_optional (
         GNUNET_JSON_spec_json ("extensions",
-                               &extensions),
-        NULL),
+                               &manifests),
+        &no_extensions),
       GNUNET_JSON_spec_mark_optional (
         GNUNET_JSON_spec_fixed_auto (
           "extensions_sig",
           &extensions_sig),
-        NULL),
+        &no_signature),
       GNUNET_JSON_spec_end ()
     };
 
@@ -918,22 +921,27 @@ decode_keys_json (const json_t *resp_obj,
                                ext_spec,
                                NULL, NULL));
 
-    if (NULL != extensions)
+
+    if (! no_extensions && no_signature)
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "found extensions without signature\n");
+
+    if (! no_extensions && ! no_signature)
     {
       /* 2. We have an extensions object. Verify its signature. */
       EXITIF (GNUNET_OK !=
-              TALER_extensions_verify_json_config_signature (
-                extensions,
+              TALER_extensions_verify_manifests_signature (
+                manifests,
                 &extensions_sig,
                 &key_data->master_pub));
 
       /* 3. Parse and set the the configuration of the extensions accordingly 
*/
       EXITIF (GNUNET_OK !=
-              TALER_extensions_load_json_config (extensions));
+              TALER_extensions_load_manifests (manifests));
     }
 
     /* 4. assuming we might have now a new value for age_mask, set it in 
key_data */
-    key_data->age_mask = TALER_extensions_age_restriction_ageMask ();
+    key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
   }
 
   /**
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index a937c18a..855b4fcc 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -236,7 +236,8 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
       struct TALER_PrivateContractHashP h_contract_terms;
       struct TALER_AgeCommitmentHash h_age_commitment;
       bool no_hac;
-      // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
+      struct TALER_ExtensionPolicyHashP h_policy;
+      bool no_h_policy;
       struct GNUNET_TIME_Timestamp wallet_timestamp;
       struct TALER_MerchantPublicKeyP merchant_pub;
       struct GNUNET_TIME_Timestamp refund_deadline;
@@ -252,6 +253,10 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
           GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
                                        &h_age_commitment),
           &no_hac),
+        GNUNET_JSON_spec_mark_optional (
+          GNUNET_JSON_spec_fixed_auto ("h_policy",
+                                       &h_policy),
+          &no_h_policy),
         GNUNET_JSON_spec_timestamp ("timestamp",
                                     &wallet_timestamp),
         GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -277,10 +282,8 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
                                        &deposit_fee,
                                        &h_wire,
                                        &h_contract_terms,
-                                       no_hac
-                                       ? NULL
-                                       : &h_age_commitment,
-                                       NULL /* FIXME #7270-OEC: h_extensions! 
*/,
+                                       no_hac ?  NULL : &h_age_commitment,
+                                       no_h_policy ? NULL: &h_policy,
                                        &h_denom_pub,
                                        wallet_timestamp,
                                        &merchant_pub,
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index dd39ee94..ec9ccb74 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -1312,9 +1312,6 @@ main (int argc,
                     "INFO",
                     NULL);
 
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_extension_age_restriction_register ());
-
   cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
   GNUNET_assert (NULL != cipher);
   uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c
index 27d47bfd..f159b2f1 100644
--- a/src/testing/test_exchange_p2p.c
+++ b/src/testing/test_exchange_p2p.c
@@ -522,9 +522,6 @@ main (int argc,
                     "INFO",
                     NULL);
 
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_extension_age_restriction_register ());
-
   cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
   GNUNET_assert (NULL != cipher);
   uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c 
b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index d99b1293..293ecba2 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -199,7 +199,7 @@ deposit_confirmation_run (void *cls,
                           const struct TALER_TESTING_Command *cmd,
                           struct TALER_TESTING_Interpreter *is)
 {
-  static struct TALER_ExtensionContractHashP no_h_extensions;
+  static struct TALER_ExtensionPolicyHashP no_h_policy;
   struct DepositConfirmationState *dcs = cls;
   const struct TALER_TESTING_Command *deposit_cmd;
   struct TALER_MerchantWireHashP h_wire;
@@ -310,7 +310,7 @@ deposit_confirmation_run (void *cls,
   }
   dcs->dc = TALER_AUDITOR_deposit_confirmation (dcs->auditor,
                                                 &h_wire,
-                                                &no_h_extensions,
+                                                &no_h_policy,
                                                 &h_contract_terms,
                                                 *exchange_timestamp,
                                                 *wire_deadline,
diff --git a/src/testing/testing_api_cmd_batch_deposit.c 
b/src/testing/testing_api_cmd_batch_deposit.c
index 967a5ac3..54a20cdb 100644
--- a/src/testing/testing_api_cmd_batch_deposit.c
+++ b/src/testing/testing_api_cmd_batch_deposit.c
@@ -381,7 +381,7 @@ batch_deposit_run (void *cls,
       .merchant_payto_uri = payto_uri,
       .wire_salt = wire_salt,
       .h_contract_terms = h_contract_terms,
-      .extension_details = NULL /* FIXME #7270-OEC */,
+      .policy_details = NULL /* FIXME #7270-OEC */,
       .timestamp = ds->wallet_timestamp,
       .merchant_pub = merchant_pub,
       .refund_deadline = ds->refund_deadline
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c 
b/src/testing/testing_api_cmd_batch_withdraw.c
index b68ca99f..a5229ae9 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
 
       acp = GNUNET_new (struct TALER_AgeCommitmentProof);
       hac = GNUNET_new (struct TALER_AgeCommitmentHash);
-      mask = TALER_extensions_age_restriction_ageMask ();
+      mask = TALER_extensions_get_age_restriction_mask ();
       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                                   &seed,
                                   sizeof(seed));
diff --git a/src/testing/testing_api_cmd_deposit.c 
b/src/testing/testing_api_cmd_deposit.c
index f6694767..16ac139f 100644
--- a/src/testing/testing_api_cmd_deposit.c
+++ b/src/testing/testing_api_cmd_deposit.c
@@ -468,7 +468,7 @@ deposit_run (void *cls,
       .merchant_payto_uri = payto_uri,
       .wire_salt = wire_salt,
       .h_contract_terms = h_contract_terms,
-      .extension_details = NULL /* FIXME #7270-OEC */,
+      .policy_details = NULL /* FIXME #7270-OEC */,
       .timestamp = ds->wallet_timestamp,
       .merchant_pub = merchant_pub,
       .refund_deadline = ds->refund_deadline
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index ce2c49cc..7a81d1c3 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
 
     acp = GNUNET_new (struct TALER_AgeCommitmentProof);
     hac = GNUNET_new (struct TALER_AgeCommitmentHash);
-    mask = TALER_extensions_age_restriction_ageMask ();
+    mask = TALER_extensions_get_age_restriction_mask ();
     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                                 &seed,
                                 sizeof(seed));
diff --git a/src/testing/testing_api_helpers_exchange.c 
b/src/testing/testing_api_helpers_exchange.c
index d813021b..7fe0038a 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls,
   char *exchange_master_pub;
   int ret;
 
-  /* Load the age restriction mask from the configuration */
-  TALER_extensions_load_taler_config (cfg);
+  /* Load the extensions */
+  if (GNUNET_OK != TALER_extensions_init (cfg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "couldn't load extensions");
+    return GNUNET_SYSERR;
+  }
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 94edac02..acafdae5 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
 
 libtalerutil_la_LIBADD = \
   -lgnunetutil \
+  -lgnunetjson \
   -lsodium \
   -ljansson \
   $(LIBGCRYPT_LIBS) \
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index b87c8543..f4ac9abe 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -21,6 +21,7 @@
 #include "platform.h"
 #include "taler_util.h"
 #include "taler_signatures.h"
+#include <gnunet/gnunet_json_lib.h>
 #include <gcrypt.h>
 
 void
@@ -436,3 +437,113 @@ TALER_age_commitment_proof_free (
     cp->commitment.keys = NULL;
   }
 }
+
+
+enum GNUNET_GenericReturnValue
+TALER_JSON_parse_age_groups (const json_t *root,
+                             struct TALER_AgeMask *mask)
+{
+  enum GNUNET_GenericReturnValue ret;
+  const char *str;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("age_groups",
+                             &str),
+    GNUNET_JSON_spec_end ()
+  };
+
+  ret = GNUNET_JSON_parse (root,
+                           spec,
+                           NULL,
+                           NULL);
+  if (GNUNET_OK == ret)
+    TALER_parse_age_group_string (str, mask);
+
+  GNUNET_JSON_parse_free (spec);
+
+  return ret;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+  const char *groups,
+  struct TALER_AgeMask *mask)
+{
+
+  const char *pos = groups;
+  unsigned int prev = 0;
+  unsigned int val = 0;
+  char c;
+
+  while (*pos)
+  {
+    c = *pos++;
+    if (':' == c)
+    {
+      if (prev >= val)
+        return GNUNET_SYSERR;
+
+      mask->bits |= 1 << val;
+      prev = val;
+      val = 0;
+      continue;
+    }
+
+    if ('0'>c || '9'<c)
+      return GNUNET_SYSERR;
+
+    val = 10 * val + c - '0';
+
+    if (0>=val || 32<=val)
+      return GNUNET_SYSERR;
+  }
+
+  if (32<=val || prev>=val)
+    return GNUNET_SYSERR;
+
+  mask->bits |= (1 << val);
+  mask->bits |= 1; // mark zeroth group, too
+
+  return GNUNET_OK;
+}
+
+
+char *
+TALER_age_mask_to_string (
+  const struct TALER_AgeMask *mask)
+{
+  uint32_t bits = mask->bits;
+  unsigned int n = 0;
+  char *buf = GNUNET_malloc (32 * 3); // max characters possible
+  char *pos = buf;
+
+  if (NULL == buf)
+  {
+    return buf;
+  }
+
+  while (bits != 0)
+  {
+    bits >>= 1;
+    n++;
+    if (0 == (bits & 1))
+    {
+      continue;
+    }
+
+    if (n > 9)
+    {
+      *(pos++) = '0' + n / 10;
+    }
+    *(pos++) = '0' + n % 10;
+
+    if (0 != (bits >> 1))
+    {
+      *(pos++) = ':';
+    }
+  }
+  return buf;
+}
+
+
+/* end util/age_restriction.c */
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index 3169cb84..1762d5a5 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -48,10 +48,10 @@ struct TALER_DepositConfirmationPS
   struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;
 
   /**
-   * Hash over the extension options of the deposit, 0 if there
-   * were not extension options.
+   * Hash over the optional policy extension of the deposit, 0 if there
+   * was no policy.
    */
-  struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED;
+  struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED;
 
   /**
    * Time when this confirmation was generated / when the exchange received
@@ -101,7 +101,7 @@ TALER_exchange_online_deposit_confirmation_sign (
   TALER_ExchangeSignCallback scb,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
@@ -123,8 +123,8 @@ TALER_exchange_online_deposit_confirmation_sign (
     .merchant_pub = *merchant_pub
   };
 
-  if (NULL != h_extensions)
-    dcs.h_extensions = *h_extensions;
+  if (NULL != h_policy)
+    dcs.h_policy = *h_policy;
   TALER_amount_hton (&dcs.amount_without_fee,
                      amount_without_fee);
   return scb (&dcs.purpose,
@@ -137,7 +137,7 @@ enum GNUNET_GenericReturnValue
 TALER_exchange_online_deposit_confirmation_verify (
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_MerchantWireHashP *h_wire,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
@@ -159,8 +159,8 @@ TALER_exchange_online_deposit_confirmation_verify (
     .merchant_pub = *merchant_pub
   };
 
-  if (NULL != h_extensions)
-    dcs.h_extensions = *h_extensions;
+  if (NULL != h_policy)
+    dcs.h_policy = *h_policy;
   TALER_amount_hton (&dcs.amount_without_fee,
                      amount_without_fee);
   if (GNUNET_OK !=
diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c
index b316c8ba..d0b644e7 100644
--- a/src/util/offline_signatures.c
+++ b/src/util/offline_signatures.c
@@ -926,10 +926,10 @@ TALER_exchange_offline_global_fee_verify (
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
- * @brief Signature made by the exchange offline key over the
- * configuration of an extension.
+ * @brief Signature made by the exchange offline key over the manifest of
+ * an extension.
  */
-struct TALER_MasterExtensionConfigurationPS
+struct TALER_MasterExtensionManifestPS
 {
   /**
    * Purpose is #TALER_SIGNATURE_MASTER_EXTENSION.   Signed
@@ -938,24 +938,24 @@ struct TALER_MasterExtensionConfigurationPS
   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
-   * Hash of the JSON object that represents the configuration of an extension.
+   * Hash of the JSON object that represents the manifests of extensions.
    */
-  struct TALER_ExtensionConfigHashP h_config GNUNET_PACKED;
+  struct TALER_ExtensionManifestsHashP h_manifest GNUNET_PACKED;
 };
 
 GNUNET_NETWORK_STRUCT_END
 
 
 void
-TALER_exchange_offline_extension_config_hash_sign (
-  const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_sign (
+  const struct TALER_ExtensionManifestsHashP *h_manifest,
   const struct TALER_MasterPrivateKeyP *master_priv,
   struct TALER_MasterSignatureP *master_sig)
 {
-  struct TALER_MasterExtensionConfigurationPS ec = {
+  struct TALER_MasterExtensionManifestPS ec = {
     .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
     .purpose.size = htonl (sizeof(ec)),
-    .h_config = *h_config
+    .h_manifest = *h_manifest
   };
   GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                             &ec,
@@ -964,16 +964,16 @@ TALER_exchange_offline_extension_config_hash_sign (
 
 
 enum GNUNET_GenericReturnValue
-TALER_exchange_offline_extension_config_hash_verify (
-  const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_verify (
+  const struct TALER_ExtensionManifestsHashP *h_manifest,
   const struct TALER_MasterPublicKeyP *master_pub,
   const struct TALER_MasterSignatureP *master_sig
   )
 {
-  struct TALER_MasterExtensionConfigurationPS ec = {
+  struct TALER_MasterExtensionManifestPS ec = {
     .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
     .purpose.size = htonl (sizeof(ec)),
-    .h_config = *h_config
+    .h_manifest = *h_manifest
   };
 
   return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 6c8124d0..5efcc5d6 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -49,9 +49,9 @@ struct TALER_DepositRequestPS
   struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED;
 
   /**
-   * Hash over extension attributes shared with the exchange.
+   * Hash over optional policy extension attributes shared with the exchange.
    */
-  struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED;
+  struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED;
 
   /**
    * Hash over the wiring information of the merchant.
@@ -120,7 +120,7 @@ TALER_wallet_deposit_sign (
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_AgeCommitmentHash *h_age_commitment,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_DenominationHashP *h_denom_pub,
   const struct GNUNET_TIME_Timestamp wallet_timestamp,
   const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -141,8 +141,8 @@ TALER_wallet_deposit_sign (
 
   if (NULL != h_age_commitment)
     dr.h_age_commitment = *h_age_commitment;
-  if (NULL != h_extensions)
-    dr.h_extensions = *h_extensions;
+  if (NULL != h_policy)
+    dr.h_policy = *h_policy;
   TALER_amount_hton (&dr.amount_with_fee,
                      amount);
   TALER_amount_hton (&dr.deposit_fee,
@@ -160,7 +160,7 @@ TALER_wallet_deposit_verify (
   const struct TALER_MerchantWireHashP *h_wire,
   const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_AgeCommitmentHash *h_age_commitment,
-  const struct TALER_ExtensionContractHashP *h_extensions,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
   const struct TALER_DenominationHashP *h_denom_pub,
   struct GNUNET_TIME_Timestamp wallet_timestamp,
   const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -178,13 +178,13 @@ TALER_wallet_deposit_verify (
     .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
     .merchant = *merchant_pub,
     .h_age_commitment = {{{0}}},
-    .h_extensions = {{{0}}}
+    .h_policy = {{{0}}}
   };
 
   if (NULL != h_age_commitment)
     dr.h_age_commitment = *h_age_commitment;
-  if (NULL != h_extensions)
-    dr.h_extensions = *h_extensions;
+  if (NULL != h_policy)
+    dr.h_policy = *h_policy;
   TALER_amount_hton (&dr.amount_with_fee,
                      amount);
   TALER_amount_hton (&dr.deposit_fee,

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