docs: developer documentation & customer-facing polish
All checks were successful
Build and Deploy / deploy (push) Successful in 45s

Code documentation:
- client.py: docstrings with Tink API docs URLs on every method
- demo.py: docstrings on all route handlers explaining Tink flow context
- webhook receiver: C# HMAC-SHA256 signature verification example

Customer-facing cleanup:
- Removed 'sales demo' / 'MoneyCapp × Tink' internal branding
- Neutral footer, consistent terminology (external_user_id, not tink_external_ref)
- Sandbox note on Step 3: anonymous flow vs production authorization_code flow
- Step 6: 'Next Steps for C#/.NET implementation' section
- demo_data.py: dynamic relative dates (no hardcoded year)
- print() → logging.getLogger, /debug-session gated behind DEMO_MODE
- Step 1 always resets session state (fresh start on every visit)
- README: neutral/collaborative tone, what-it-is-not section

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Henrik Jess Nielsen
2026-05-23 02:08:27 +02:00
parent bf61790465
commit f13eb21bb6
9 changed files with 238 additions and 100 deletions

View File

@@ -37,7 +37,7 @@
<div class="w-8 h-8 rounded-lg bg-violet-600 flex items-center justify-center text-white font-bold text-sm">T</div>
<div>
<span class="font-semibold text-white">Tink API Demo</span>
<span class="text-slate-400 text-sm ml-2">MoneyCapp × Tink</span>
<span class="text-slate-400 text-sm ml-2">Open Banking Demo</span>
</div>
</a>
<div class="flex items-center gap-3">
@@ -71,7 +71,7 @@
<!-- Footer -->
<footer class="border-t border-slate-800 text-center text-slate-500 text-xs py-4">
Tink API Demo &mdash; MoneyCapp sales prototype &mdash; i80.dk
Tink Open Banking API Demo
</footer>
<script>

View File

@@ -117,6 +117,19 @@
{% else %}
<!-- Not yet connected — show connection UI -->
<!-- Sandbox note: anonymous flow vs production -->
<div class="bg-amber-950/40 border border-amber-700/40 rounded-xl px-5 py-3.5 flex items-start gap-3 text-sm">
<svg class="w-4 h-4 text-amber-400 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
<div class="text-amber-200/80 leading-relaxed">
<span class="font-semibold text-amber-300">Sandbox-note:</span>
Dette demo bruger <em>anonymous Tink Link flow</em> (ingen <code class="font-mono text-xs">authorization_code</code> i URL'en) —
en sandbox-begrænsning. I produktion <strong>skal</strong> du kalde
<code class="font-mono text-xs">authorization-grant/delegate</code> og sende koden med til Tink Link,
så bank-forbindelsen bindes til den korrekte Tink-bruger.
<a href="https://docs.tink.com/resources/tink-link/tink-link-web-permanent-users" target="_blank" class="text-amber-400 underline hover:text-amber-300 ml-1">Docs ↗</a>
</div>
</div>
<!-- PRIMARY: direct callback flow -->
<div class="bg-slate-900 border border-emerald-700/40 rounded-xl p-5 space-y-4">
<div class="flex items-start gap-3">

View File

@@ -38,9 +38,11 @@
<span class="ml-2 text-xs px-2 py-0.5 rounded-full font-mono font-semibold bg-slate-800 text-slate-400 border border-slate-700">v1</span>
</div>
<p class="text-slate-400 text-sm mt-2 max-w-2xl">
Opret en Tink-bruger med en <code class="text-violet-300">tink_external_ref</code>
MoneyCapp's interne reference til kunden i Tink. Gemmes i jeres kundedatabase som <code class="text-violet-300">tink_external_ref</code> (ikke jeres interne <code class="text-slate-400">customer_id</code>).
Tink returnerer et <code class="text-violet-300">user_id</code> som bruges i efterfølgende kald.
Opret en Tink-bruger med et <code class="text-violet-300">external_user_id</code>
jeres interne reference til kunden (f.eks. et kundenummer eller UUID fra jeres database).
Gem dette som en kolonne i jeres kundedatabase (<code class="text-violet-300">tink_user_ref</code> el.lign.) —
det er ikke det samme som jeres interne <code class="text-slate-400">customer_id</code>.
Tink returnerer et <code class="text-violet-300">user_id</code> (UUID) der bruges i efterfølgende kald.
</p>
</div>
@@ -89,8 +91,8 @@
<div class="px-5 py-4 border-b border-slate-800">
<p class="text-sm font-semibold text-white">Hvem opretter vi?</p>
<p class="text-xs text-slate-400 mt-0.5">
<code class="text-violet-300">tink_external_ref</code> = jeres reference til kunden i Tink
adskilt fra jeres interne <code class="text-slate-400">customer_id</code>
<code class="text-violet-300">external_user_id</code> = jeres interne kundereference
gemmes i jeres database som f.eks. <code class="text-slate-400">tink_user_ref</code>
</p>
</div>
<form method="POST" action="/demo/step/2" class="p-5 space-y-4">
@@ -101,7 +103,7 @@
value="Henrik Jess"
class="w-full bg-slate-950 border border-slate-700 rounded-lg px-4 py-2.5 text-white text-sm font-mono
placeholder-slate-600 focus:outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500/30 transition">
<p class="text-xs text-slate-600 mt-1.5"><code class="text-violet-300/70">tink_external_ref</code> = <code class="text-violet-300/70">moneycapp-henrik-jess-a3f9c1</code></p>
<p class="text-xs text-slate-600 mt-1.5"><code class="text-violet-300/70">external_user_id</code> = <code class="text-violet-300/70">moneycapp-henrik-jess-a3f9c1</code></p>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2">Marked</label>
@@ -136,13 +138,13 @@
<p class="text-xs text-slate-500 uppercase tracking-wider mb-3">API endpoint</p>
<code class="text-emerald-400 font-mono text-sm">POST https://api.tink.com/api/v1/user/create</code>
<div class="mt-3 bg-slate-950 rounded-lg p-3 overflow-x-auto">
<pre class="text-xs text-amber-300 font-mono whitespace-pre"># MoneyCapp DB:
# customer_id = 42 ← jeres interne ID (Tink ser det aldrig)
# tink_external_ref = "moneycapp-42-a3f9c1" ← Tink-reference
<pre class="text-xs text-amber-300 font-mono whitespace-pre"># Your DB schema (example):
# customer_id = 42 your internal ID (Tink never sees this)
# tink_user_ref = "moneycapp-42-a3f9c1" ← store Tink's external_user_id here
# Request body
{
"external_user_id": "moneycapp-&lt;ref&gt;", ← tink_external_ref
"external_user_id": "moneycapp-&lt;ref&gt;", ← your reference, stored in DB
"market": "DK",
"locale": "da_DK"
}

View File

@@ -223,8 +223,7 @@ async def tink_webhook(request: Request):
Fra brugeroprettelse over live bankdata til real-time webhooks — alt via Tink API.
</p>
<p class="text-slate-500 text-sm mb-6 max-w-xl mx-auto">
Vi har vist: auth → bruger → bank-tilslutning → konti (v2) → transaktioner (v2) → webhooks.
Det er præcis hvad MoneyCapp mangler for at gøre deres integration robust.
Auth → bruger → bank-tilslutning → konti (v2) → transaktioner (v2) → webhooks.
</p>
<div class="flex gap-3 justify-center flex-wrap">
<a href="/demo/reset" class="px-5 py-2.5 border border-slate-600 text-slate-300 hover:text-white hover:border-slate-400 rounded-xl text-sm transition">↺ Kør demo igen</a>
@@ -235,6 +234,33 @@ async def tink_webhook(request: Request):
</div>
</div>
<!-- Next Steps for implementation -->
<div class="mt-6 bg-slate-900/60 border border-slate-800 rounded-2xl p-7">
<h4 class="text-white font-semibold text-base mb-4 flex items-center gap-2">
<svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
Næste skridt mod en produktion-klar integration
</h4>
<div class="grid md:grid-cols-2 gap-3 text-sm text-slate-400">
<div class="bg-slate-800/60 rounded-lg p-4 space-y-1.5">
<p class="text-violet-300 font-semibold text-xs uppercase tracking-wider mb-2">Backend (C# / .NET)</p>
<p>1. Lav en <code class="text-violet-300 font-mono text-xs">TinkApiClient</code> wrapper (brug <code class="text-xs font-mono">tink/client.py</code> som reference)</p>
<p>2. Gem <code class="text-violet-300 font-mono text-xs">external_user_id</code> + <code class="text-violet-300 font-mono text-xs">user_id</code> i din kundedatabase</p>
<p>3. Implementér <code class="text-violet-300 font-mono text-xs">/callback</code> endpoint med token exchange</p>
<p>4. Gem tokens sikkert (encrypted, server-side — ikke i cookie)</p>
</div>
<div class="bg-slate-800/60 rounded-lg p-4 space-y-1.5">
<p class="text-violet-300 font-semibold text-xs uppercase tracking-wider mb-2">Webhooks & Production</p>
<p>5. Byg webhook receiver med <a href="https://docs.tink.com/api#webhook/webhook-endpoints" target="_blank" class="text-violet-400 hover:text-violet-300 underline">HMAC-SHA256 signature verification</a></p>
<p>6. Skift til production Tink-credentials (Tink Console)</p>
<p>7. Registrér din production callback URI i Tink Console</p>
<p>8. Brug <code class="text-violet-300 font-mono text-xs">authorization-grant/delegate</code> i prod-flowet (ikke anon)</p>
</div>
</div>
<p class="text-xs text-slate-600 mt-4">
Kildekoden til dette demo (<code class="font-mono">src/tink/client.py</code> og <code class="font-mono">src/routes/demo.py</code>) er skrevet for at være letlæselig og direkte overførbar til andre platforme.
</p>
</div>
<!-- Navigation -->
<div class="mt-4 flex justify-start">
<a href="/demo/step/5"