Index: src/routes/admin-paid.js
===================================================================
--- src/routes/admin-paid.js	(revision 603d2461f6fb57f0422bea17aad09eef157f788f)
+++ src/routes/admin-paid.js	(revision c3d12a6f5721ed2faee19362b8d7e0caeea854d5)
@@ -60,4 +60,7 @@
       accessToken: (b.access_token || '').trim() || undefined,
       refreshToken: (b.refresh_token || '').trim() || undefined,
+      // Empty clears it (null), a value sets it. Unlike secrets, this is not
+      // sensitive and there's a clear "remove the link" intent.
+      patreonUrl: (b.patreon_url || '').trim() || null,
       defaultMinCents: Number.isFinite(cents) ? cents : undefined,
     });
Index: src/routes/paid.js
===================================================================
--- src/routes/paid.js	(revision 603d2461f6fb57f0422bea17aad09eef157f788f)
+++ src/routes/paid.js	(revision c3d12a6f5721ed2faee19362b8d7e0caeea854d5)
@@ -54,9 +54,12 @@
   const payload = verifyBlob(String(req.query.state || ''));
   if (!payload || payload.purpose !== 'patron' || payload.siteId !== r.site.id) {
-    return res.status(400).send('Ongeldige of verlopen aanvraag. Probeer opnieuw vanaf de post.');
+    return renderPage(req, res, 'pages/paid-result', {
+      pageTitle: 'Ontgrendelen', bodyClass: 'on-special', ok: false, reason: 'expired',
+    });
   }
+  const patronUrl = PaidPatreon.patreonUrl(r.site.id);
   const code = String(req.query.code || '');
   if (req.query.error || !code) {
-    return renderPage(req, res, 'pages/paid-result', { pageTitle: 'Ontgrendelen', bodyClass: 'on-special', ok: false, reason: 'declined', postSlug: payload.post });
+    return renderPage(req, res, 'pages/paid-result', { pageTitle: 'Ontgrendelen', bodyClass: 'on-special', ok: false, reason: 'declined', postSlug: payload.post, patronUrl });
   }
   const membership = await PaidPatreon.verifyPatron(r.site.id, code, baseUrl(req) + '/paid/callback').catch(() => null);
@@ -66,5 +69,5 @@
     return renderPage(req, res, 'pages/paid-result', {
       pageTitle: 'Ontgrendelen', bodyClass: 'on-special', ok: false,
-      reason: active ? 'tier' : 'notpatron', neededCents: payload.cents, haveCents: cents, postSlug: payload.post,
+      reason: active ? 'tier' : 'notpatron', neededCents: payload.cents, haveCents: cents, postSlug: payload.post, patronUrl,
     });
   }
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 603d2461f6fb57f0422bea17aad09eef157f788f)
+++ src/routes/posts.js	(revision c3d12a6f5721ed2faee19362b8d7e0caeea854d5)
@@ -21,5 +21,5 @@
 import ActivityPubService from '../services/ActivityPubService.js';
 import { premiumUnlocked } from '../services/PatreonService.js';
-import { defaultMinCents as paidDefaultMinCents } from '../services/PaidPatreonService.js';
+import { defaultMinCents as paidDefaultMinCents, patreonUrl as paidPatronUrl } from '../services/PaidPatreonService.js';
 import MusicMeta from '../services/MusicMeta.js';
 
@@ -1247,4 +1247,5 @@
       pgCents: post.paid_min_cents || paidDefaultMinCents(site.id),
       pgSlug: post.slug,
+      pgPatronUrl: paidPatronUrl(site.id),
       newerPost,
       olderPost,
