diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index 5f27eb1a7b5915dbe01785bf60f2fc8360655f91..dce8feab367d241aa4991f2c87769fcd6bf42562 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -22,8 +22,11 @@ For example::
 
   [storageclient.plugins.privatestorageio-zkapauthz-v1]
   redeemer = dummy
+  issuer-public-key = YXNkYXNkYXNkYXNkYXNkCg==
 
-A value of ``ristretto`` causes the client to speak Ristretto-flavored PrivacyPass to an issuer server.
+The value of the ``issuer-public-key`` item is included as-is as the public key in the successful redemption response.
+
+A ``redeemer`` value of ``ristretto`` causes the client to speak Ristretto-flavored PrivacyPass to an issuer server.
 In this case, the ``ristretto-issuer-root-url`` item is also required.
 
 For example::
diff --git a/src/_zkapauthorizer/controller.py b/src/_zkapauthorizer/controller.py
index 15d8395080c10777a57cd227fa05e70ffdc301a5..69571ca2c03ab26d3389775df4849f1134b0e256 100644
--- a/src/_zkapauthorizer/controller.py
+++ b/src/_zkapauthorizer/controller.py
@@ -372,12 +372,25 @@ class DummyRedeemer(object):
     A ``DummyRedeemer`` pretends to redeem vouchers for ZKAPs.  Instead of
     really redeeming them, it makes up some fake ZKAPs and pretends those are
     the result.
+
+    :ivar unicode _public_key: The base64-encoded public key to return with
+        all successful redemption results.  As with the tokens returned by
+        this redeemer, chances are this is not actually a valid public key.
+        Its corresponding private key certainly has not been used to sign
+        anything.
     """
-    _public_key = attr.ib(default=None)
+    _public_key = attr.ib(
+        validator=attr.validators.instance_of(unicode),
+    )
 
     @classmethod
     def make(cls, section_name, node_config, announcement, reactor):
-        return cls()
+        return cls(
+            node_config.get_config(
+                section=section_name,
+                option=u"issuer-public-key",
+            ).decode(u"utf-8"),
+        )
 
     def random_tokens_for_voucher(self, voucher, counter, count):
         """
diff --git a/src/_zkapauthorizer/tests/fixtures.py b/src/_zkapauthorizer/tests/fixtures.py
index 35aadaea07c79020433806bdcf6ccea6cb2e4410..1b226500bc327358f4dfa3ee28b981b209ca9d88 100644
--- a/src/_zkapauthorizer/tests/fixtures.py
+++ b/src/_zkapauthorizer/tests/fixtures.py
@@ -20,6 +20,10 @@ from __future__ import (
     absolute_import,
 )
 
+from base64 import (
+    b64encode,
+)
+
 import attr
 
 from fixtures import (
@@ -43,6 +47,7 @@ from ..model import (
     memory_connect,
 )
 from ..controller import (
+    DummyRedeemer,
     PaymentController,
 )
 
@@ -99,8 +104,12 @@ class ConfiglessMemoryVoucherStore(Fixture):
     This is like ``TemporaryVoucherStore`` but faster because it skips the
     Tahoe-LAFS parts.
     """
-    redeemer = attr.ib()
     get_now = attr.ib()
+    _public_key = attr.ib(default=b64encode(b"A" * 32).decode("utf-8"))
+    redeemer = attr.ib(default=None, init=False)
+
+    def __attrs_post_init__(self):
+        self.redeemer = DummyRedeemer(self._public_key)
 
     def _setUp(self):
         here = FilePath(u".")
diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py
index 77c854a2bff7109130f8b41257fbca9671ce3262..c9f46fd644aeef83637453baeb643e5c8001cfc7 100644
--- a/src/_zkapauthorizer/tests/strategies.py
+++ b/src/_zkapauthorizer/tests/strategies.py
@@ -245,10 +245,12 @@ def client_dummyredeemer_configurations():
     """
     Build DummyRedeemer-using configuration values for the client-side plugin.
     """
-    return just({
-        u"redeemer": u"dummy",
-        u"default-token-count": u"32",
-    })
+    return dummy_ristretto_keys().map(
+        lambda key: {
+            u"redeemer": u"dummy",
+            u"issuer-public-key": key,
+            u"default-token-count": u"32",
+        })
 
 
 def token_counts():
diff --git a/src/_zkapauthorizer/tests/test_client_resource.py b/src/_zkapauthorizer/tests/test_client_resource.py
index 5c18d34e83c6b8884e827febc7713e261b4d0103..79c1cdcd2eec1c0a2905183be9a67631727582f8 100644
--- a/src/_zkapauthorizer/tests/test_client_resource.py
+++ b/src/_zkapauthorizer/tests/test_client_resource.py
@@ -192,6 +192,32 @@ from .json import (
 
 TRANSIENT_ERROR = u"something went wrong, who knows what"
 
+def get_dummyredeemer_public_key(plugin_name, node_config):
+    """
+    Get the issuer public key a ``DummyRedeemer`` has been configured with.
+
+    :param unicode plugin_name: The plugin name to use to choose a
+        configuration section.
+
+    :param _Config node_config: See ``from_configuration``.
+    """
+    section_name = u"storageclient.plugins.{}".format(plugin_name)
+    redeemer_kind = node_config.get_config(
+        section=section_name,
+        option=u"redeemer",
+    )
+    if redeemer_kind != "dummy":
+        raise ValueError(
+            "Cannot read dummy redeemer public key from configuration for {!r} redeemer.".format(
+                redeemer_kind,
+            ),
+        )
+    return node_config.get_config(
+        section=section_name,
+        option=u"issuer-public-key",
+    ).decode("utf-8")
+
+
 # Helper to work-around https://github.com/twisted/treq/issues/161
 def uncooperator(started=True):
     return Cooperator(
@@ -1108,6 +1134,7 @@ class VoucherTests(TestCase):
         are included in a json-encoded response body.
         """
         count = get_token_count("privatestorageio-zkapauthz-v1", config)
+        public_key = get_dummyredeemer_public_key("privatestorageio-zkapauthz-v1", config)
         return self._test_get_known_voucher(
             config,
             api_auth_token,
@@ -1120,7 +1147,7 @@ class VoucherTests(TestCase):
                 state=Equals(Redeemed(
                     finished=now,
                     token_count=count,
-                    public_key=None,
+                    public_key=public_key,
                 )),
             ),
         )
@@ -1286,6 +1313,7 @@ class VoucherTests(TestCase):
         vouchers.
         """
         count = get_token_count("privatestorageio-zkapauthz-v1", config)
+        public_key = get_dummyredeemer_public_key("privatestorageio-zkapauthz-v1", config)
         return self._test_list_vouchers(
             config,
             api_auth_token,
@@ -1300,7 +1328,7 @@ class VoucherTests(TestCase):
                         state=Redeemed(
                             finished=now,
                             token_count=count,
-                            public_key=None,
+                            public_key=public_key,
                         ),
                     ).marshal()
                     for voucher
diff --git a/src/_zkapauthorizer/tests/test_controller.py b/src/_zkapauthorizer/tests/test_controller.py
index 25568d09803c21def1bf10a49d7b088e6bf00895..73398aabd9fcbdaecdb4e8b4ed4c0dcbb27b2ff8 100644
--- a/src/_zkapauthorizer/tests/test_controller.py
+++ b/src/_zkapauthorizer/tests/test_controller.py
@@ -220,8 +220,8 @@ class PaymentControllerTests(TestCase):
     """
     Tests for ``PaymentController``.
     """
-    @given(tahoe_configs(), datetimes(), vouchers())
-    def test_should_not_redeem(self, get_config, now, voucher):
+    @given(tahoe_configs(), datetimes(), vouchers(), dummy_ristretto_keys())
+    def test_should_not_redeem(self, get_config, now, voucher, public_key):
         """
         ``PaymentController.redeem`` raises ``ValueError`` if passed a voucher in
         a state when redemption should not be started.
@@ -229,7 +229,7 @@ class PaymentControllerTests(TestCase):
         store = self.useFixture(TemporaryVoucherStore(get_config, lambda: now)).store
         controller = PaymentController(
             store,
-            DummyRedeemer(),
+            DummyRedeemer(public_key),
             default_token_count=100,
             clock=Clock(),
         )
@@ -280,8 +280,8 @@ class PaymentControllerTests(TestCase):
             Equals(model_Pending(counter=0)),
         )
 
-    @given(tahoe_configs(), datetimes(), vouchers(), voucher_counters())
-    def test_redeeming(self, get_config, now, voucher, num_successes):
+    @given(tahoe_configs(), datetimes(), vouchers(), voucher_counters(), dummy_ristretto_keys())
+    def test_redeeming(self, get_config, now, voucher, num_successes, public_key):
         """
         A ``Voucher`` is marked redeeming while ``IRedeemer.redeem`` is actively
         working on redeeming it with a counter value that reflects the number
@@ -292,7 +292,7 @@ class PaymentControllerTests(TestCase):
         # that.
         counter = num_successes + 1
         redeemer = IndexedRedeemer(
-            [DummyRedeemer()] * num_successes +
+            [DummyRedeemer(public_key)] * num_successes +
             [NonRedeemer()],
         )
         store = self.useFixture(TemporaryVoucherStore(get_config, lambda: now)).store
@@ -327,8 +327,9 @@ class PaymentControllerTests(TestCase):
         vouchers(),
         voucher_counters(),
         voucher_counters().map(lambda v: v + 1),
+        dummy_ristretto_keys(),
     )
-    def test_restart_redeeming(self, get_config, now, voucher, before_restart, after_restart):
+    def test_restart_redeeming(self, get_config, now, voucher, before_restart, after_restart, public_key):
         """
         If some redemption groups for a voucher have succeeded but the process is
         interrupted, redemption begins at the first incomplete redemption
@@ -354,7 +355,7 @@ class PaymentControllerTests(TestCase):
                 store,
                 # It will let `before_restart` attempts succeed before hanging.
                 IndexedRedeemer(
-                    [DummyRedeemer()] * before_restart +
+                    [DummyRedeemer(public_key)] * before_restart +
                     [NonRedeemer()] * after_restart,
                 ),
                 default_token_count=num_tokens,
@@ -375,7 +376,7 @@ class PaymentControllerTests(TestCase):
                 # not succeed or did not get started on the first try.
                 IndexedRedeemer(
                     [NonRedeemer()] * before_restart +
-                    [DummyRedeemer()] * after_restart,
+                    [DummyRedeemer(public_key)] * after_restart,
                 ),
                 # The default token count for this new controller doesn't
                 # matter.  The redemption attempt already started with some
@@ -398,7 +399,7 @@ class PaymentControllerTests(TestCase):
                 model_Redeemed(
                     finished=now,
                     token_count=num_tokens,
-                    public_key=None,
+                    public_key=public_key,
                 ),
             ),
         )
@@ -489,8 +490,8 @@ class PaymentControllerTests(TestCase):
             ),
         )
 
-    @given(tahoe_configs(), datetimes(), vouchers())
-    def test_redeem_pending_on_startup(self, get_config, now, voucher):
+    @given(tahoe_configs(), datetimes(), vouchers(), dummy_ristretto_keys())
+    def test_redeem_pending_on_startup(self, get_config, now, voucher, public_key):
         """
         When ``PaymentController`` is created, any vouchers in the store in the
         pending state are redeemed.
@@ -520,7 +521,7 @@ class PaymentControllerTests(TestCase):
         # `__init__` side-effect. :/
         success_controller = PaymentController(
             store,
-            DummyRedeemer(),
+            DummyRedeemer(public_key),
             default_token_count=100,
             clock=Clock(),
         )
diff --git a/src/_zkapauthorizer/tests/test_model.py b/src/_zkapauthorizer/tests/test_model.py
index 46a794e7f99d09f151ebd876242ce831a1ebb11c..6a0d4450f68f00818af4b10ddfaf3634f430a865 100644
--- a/src/_zkapauthorizer/tests/test_model.py
+++ b/src/_zkapauthorizer/tests/test_model.py
@@ -95,9 +95,6 @@ from ..model import (
     LeaseMaintenanceActivity,
     memory_connect,
 )
-from ..controller import (
-    DummyRedeemer,
-)
 from .strategies import (
     tahoe_configs,
     vouchers,
@@ -425,9 +422,7 @@ class UnblindedTokenStateMachine(RuleBasedStateMachine):
     def __init__(self, case):
         super(UnblindedTokenStateMachine, self).__init__()
         self.case = case
-        self.redeemer = DummyRedeemer()
         self.configless = ConfiglessMemoryVoucherStore(
-            self.redeemer,
             # Time probably not actually relevant to this state machine.
             datetime.now,
         )
diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py
index 1d460324c840460b7c5162bdea0ae677adb96d39..0db1a375d2ffe5c1c7143920909f4a346f6791c5 100644
--- a/src/_zkapauthorizer/tests/test_plugin.py
+++ b/src/_zkapauthorizer/tests/test_plugin.py
@@ -156,6 +156,7 @@ from .strategies import (
     sizes,
     pass_counts,
     ristretto_signing_keys,
+    dummy_ristretto_keys,
 )
 from .matchers import (
     Provides,
@@ -469,6 +470,7 @@ class ClientPluginTests(TestCase):
         announcement=announcements(),
         voucher=vouchers(),
         num_passes=pass_counts(),
+        public_key=dummy_ristretto_keys(),
     )
     @capture_logging(lambda self, logger: logger.validate())
     def test_unblinded_tokens_spent(
@@ -479,6 +481,7 @@ class ClientPluginTests(TestCase):
             announcement,
             voucher,
             num_passes,
+            public_key,
     ):
         """
         The ``ZKAPAuthorizerStorageServer`` returned by ``get_storage_client``
@@ -494,7 +497,7 @@ class ClientPluginTests(TestCase):
 
         controller = PaymentController(
             store,
-            DummyRedeemer(),
+            DummyRedeemer(public_key),
             default_token_count=num_passes,
             num_redemption_groups=1,
             clock=Clock(),
diff --git a/src/_zkapauthorizer/tests/test_spending.py b/src/_zkapauthorizer/tests/test_spending.py
index e55f289a3936a709566101f2effe35fecb2855dc..bb52eff98f14da6c06e63ed61aa77fbacb228321 100644
--- a/src/_zkapauthorizer/tests/test_spending.py
+++ b/src/_zkapauthorizer/tests/test_spending.py
@@ -51,9 +51,6 @@ from .matchers import (
 from .fixtures import (
     ConfiglessMemoryVoucherStore,
 )
-from ..controller import (
-    DummyRedeemer,
-)
 from ..spending import (
     IPassGroup,
     SpendingController,
@@ -71,7 +68,6 @@ class PassGroupTests(TestCase):
         """
         configless = self.useFixture(
             ConfiglessMemoryVoucherStore(
-                DummyRedeemer(),
                 lambda: now,
             ),
         )
@@ -109,7 +105,6 @@ class PassGroupTests(TestCase):
     ):
         configless = self.useFixture(
             ConfiglessMemoryVoucherStore(
-                DummyRedeemer(),
                 lambda: now,
             ),
         )