Ermittelt aus einer Shop-URL, welcher Zahlungsdienstleister dahintersteckt — dazu die angebotenen Zahlungsarten, das Shop-System, und zu jedem Fund einen Beleg mit Confidence-Score.
psp-detect https://beispielshop.debeispielshop.de
├─ Shop-System Shopware 98% sicher
├─ Zahlungs-PSP Unzer 96% sicher
│ └─ Beleg: api.unzer.com (Request im Checkout)
├─ Zahlungsarten PayPal, Klarna, Rechnungskauf, SEPA
└─ Checkout erreicht ✓ 41.2 s
Der Zahlungsdienstleister ist auf der Startseite eines Shops fast immer unsichtbar. Er lädt erst, wenn ein Kunde im Checkout steht. Ein reiner HTML-Scan findet deshalb typischerweise nur PayPal-Buttons und Trust-Badges — also Marketing, nicht die tatsächliche Abwicklung.
Drei Fehlerquellen, die dieses Tool bewusst umgeht:
| Fehlerquelle | Beispiel | Umgang |
|---|---|---|
| Zahlungsart ≠ PSP | Ein PayPal-Button heisst nicht, dass PayPal der Acquirer ist | Feld role: gateway / wallet / method |
| Verschachtelung | Shopify Payments ist technisch Stripe | Feld underlying führt den Unterbau mit |
| Badge-Rauschen | Visa-Logo im Footer sagt nichts aus | Reine Bild-/Text-Treffer wiegen ≤ 20 und entscheiden nie allein |
Eine fünfstufige Kaskade. Jede Stufe sammelt Evidenz, keine Urteile — erst Stufe 4 entscheidet.
| Stufe | Was passiert | Warum |
|---|---|---|
| 0 Normalisierung | URL säubern, Redirects folgen, robots.txt lesen |
Zielt auf die echte finale Domain |
| 1 Passive Signale | CSP-Header, HTML, Cookies, well-known-Pfade | Der CSP-Header ist die unterschätzteste Quelle überhaupt: Shops müssen ihre PSP-Domains dort whitelisten. frame-src https://*.adyen.com ist praktisch ein Geständnis |
| 2 Rendering | Playwright lädt Start- und Produktseite, schneidet jeden Request mit | PSP-SDKs laden oft erst per JavaScript nach |
| 3 Checkout-Simulation | Warenkorb füllen, bis zur Zahlungsauswahl navigieren | Hier liegt der eigentliche Erkenntnisgewinn |
| 4 Fusion | Evidenz gewichten, Konflikte auflösen, Confidence berechnen | Siehe unten |
Addition lügt. Fünf schwache Indizien ergäben 5 × 20 = 100 und damit scheinbare Gewissheit. Stattdessen:
p = 1 − Π(1 − wᵢ · decay^i)
Ein einzelner harter Treffer (Live-Key im Quelltext, Request an die PSP-API) reicht allein. Viele weiche Treffer landen ehrlich bei "wahrscheinlich" statt bei "sicher". Das Tool sagt lieber unbekannt mit Begründung, als zu raten.
Voraussetzung: Python ≥ 3.12 und uv.
git clone https://github.com/roccostrobel/psp-detector.git
cd psp-detector
uv venv --python 3.12
uv pip install -e ".[dev,web]"
.venv/bin/playwright install chromiumStolperfalle: Vor
uv pip installmussVIRTUAL_ENVunsetzt sein, sonst landet die Installation in einer fremden venv statt in der des Projekts.
psp-detect uiÖffnet http://localhost:8765 im Browser: URL einfügen, Tiefe wählen, Ergebnis mit aufklappbarer Belegkette. Läuft ausschliesslich lokal, kein Login, keine Aussenanbindung.
Die Gestaltung folgt dem Unzer-Corporate-Design. Die Werte in web/theme.py sind nicht geschätzt, sondern mit scripts/extract_brand.py aus der gerenderten Website ausgelesen. Unzer setzt die lizenzpflichtige Schrift Visuelt ein, die sich nicht mitliefern lässt — die Schriftliste greift sie automatisch ab, wenn sie lokal installiert ist, und fällt sonst sauber auf die Systemschrift zurück.
Weil ein voller Scan mehrere Minuten dauert, läuft er als Hintergrund-Job mit Fortschrittsanzeige — ein synchroner Request würde in der Zeit abgeschnitten.
# Einzelabfrage, volle Tiefe
psp-detect scan https://beispielshop.de
# Ohne Checkout-Simulation (schneller, weniger genau)
psp-detect scan https://beispielshop.de --no-checkout
# Nur so tief wie nötig — Checkout nur bei unsicherem Ergebnis
psp-detect scan https://beispielshop.de --auto-depth
# Massenlauf
psp-detect batch shops.csv -o ergebnisse.csv --concurrency 4
# Erkennungsgüte gegen das Golden-Set messen
psp-detect eval
# Signatur-Datenbank prüfen und auflisten
psp-detect signatures --checkAusgabe als JSON mit --json, dann ist jeder Fund maschinenlesbar inklusive Belegkette.
Erkennungsregeln stehen in YAML unter src/psp_detector/signatures/, nicht im Code. Einen neuen Anbieter zu ergänzen heisst: einen Block schreiben.
- id: beispiel_psp
name: Beispiel PSP
role: gateway
regions: ["DE"]
signals:
- { type: network_host, pattern: api.beispiel-psp.de, weight: 96 }
- { type: csp_domain, pattern: "*.beispiel-psp.de", weight: 72 }
- { type: html_regex, pattern: 'bsp_live_[0-9a-f]{32}', weight: 99 }Gewichtungsleitfaden
| Bereich | Bedeutung | Beispiel |
|---|---|---|
| 95–99 | Beweiskraft | Live-Key im Quelltext, Request an die Zahlungs-API |
| 80–94 | Sehr stark | PSP-eigenes SDK, Checkout-Iframe |
| 60–79 | Stark | CSP-Whitelist-Eintrag, PSP-spezifisches Cookie |
| 30–59 | Indiz | Assets, CDN-Hosts, DOM-Klassen |
| 1–29 | Schwach | Sichtbarer Text, Logos — nie allein entscheidend |
Abgedeckt sind derzeit ~50 Signaturen mit DACH-Schwerpunkt: Unzer, Computop, PAYONE, Novalnet, secupay, VR Payment, Datatrans, Saferpay, wallee, mPAY24 — plus die internationalen Gateways und die im deutschsprachigen Raum verbreiteten Shop-Systeme (Shopware, JTL, OXID, plentymarkets, Gambio).
Solche Tools scheitern normalerweise nicht daran, dass sie nicht laufen, sondern daran, dass sie plausibel falsch liegen. Gegenmassnahmen:
- Golden-Set — Shops mit manuell verifiziertem PSP, nachgerechnet bei jeder Änderung
- Eingefrorene Fixtures — Observations sind serialisierbar, Tests laufen offline und deterministisch
- Metrik-Report —
psp-detect evalgibt Precision und Recall je Anbieter aus, Regression wird sichtbar - Ehrliche Unsicherheit —
unbekanntmit Begründung statt geratenem Treffer
Zielwert v1.0: Recall ≥ 90 %, Precision ≥ 95 % auf dem Golden-Set.
Nicht optional, sondern Teil der Definition von "sauber":
- Rate-Limit pro Domain, keine parallelen Sessions gegen denselben Shop
robots.txtwird gelesen und standardmässig respektiert- Identifizierbarer User-Agent
- Niemals eine Bestellung abschliessen, niemals Zahlungsdaten eingeben, niemals Konten anlegen — harter Stopp bei der Zahlungsauswahl
- Keine Speicherung personenbezogener Daten; alle ausgewerteten Informationen sind öffentlich einsehbar
- Es werden keine Zugangsbeschränkungen umgangen
MIT — siehe LICENSE.