StiftOS FörderPortal

Vollständiges Benutzerhandbuch für Installation, Konfiguration und täglichen Betrieb des PHP-basierten Förder- und Spendenmanagement-Portals für Stiftungen und gemeinnützige Organisationen.

📄 Version 1.0 PHP 8.2+ SQLite 3 Apache / Nginx

Das StiftOS FörderPortal ist eine selbst gehostete Webanwendung, die Stiftungen, Bürgerstiftungen und NGOs dabei unterstützt, Förderanträge digital zu verwalten, Spenden zu erfassen und Zuwendungsbestätigungen automatisiert zu erstellen. Das System ist als White-Label-Lösung konzipiert und kann vollständig an das Corporate Design Ihrer Organisation angepasst werden.

📋
Digitales Antragsmanagement mit Statusverfolgung
🏛️
Vorstandssitzungen & Abstimmungsworkflow
💶
Spendenverwaltung mit SEPA-Mandate
📄
PDF-Generierung: Bescheide & Zuwendungsbestätigungen
📧
Automatisierte E-Mail-Benachrichtigungen
🔐
TOTP 2FA für alle Adminkonten
🔗
REST-API für FluentForms & Webhook-Integration
🎨
White-Label Branding & CSS-Variablen

1 Installation & Ersteinrichtung

Systemvoraussetzungen

Bevor Sie mit der Installation beginnen, stellen Sie sicher, dass Ihr Server die folgenden Mindestanforderungen erfüllt:

KomponenteMinimumEmpfohlenHinweis
PHP8.28.3 oder 8.4Getestet bis PHP 8.5
PHP Extension: pdo_sqlitePflichtFür SQLite-Datenbankzugriff
PHP Extension: mbstringPflichtMultibyte-Zeichenketten
PHP Extension: jsonPflichtAPI & Konfiguration
PHP Extension: opensslPflichtSMTP-Verschlüsselung, TOTP
WebserverApache 2.4 + mod_rewriteOder Nginx mit location-Block
Festplatte50 MB500 MB+Wächst mit PDF-Archiv
RAM128 MB PHP limit256 MB+PDF-Generierung ist speicherintensiv
ℹ️
Überprüfen Sie installierte PHP-Extensions mit php -m | grep -E "pdo_sqlite|mbstring|json|openssl" auf der Kommandozeile oder über phpinfo() im Browser.

Download & Dateistruktur

Nach dem Entpacken des Archivs finden Sie folgende Verzeichnisstruktur im Hauptordner foerderportal/:

foerderportal/
├── index.php              # Einziger Einstiegspunkt (Single Entry Point)
├── setup.php              # Ersteinrichtungsassistent (danach absichern!)
├── .htaccess              # Apache URL-Rewriting + Sicherheitsregeln
├── nginx-security.conf    # Nginx-Konfigurationsblock zum Einbinden
│
├── includes/              # PHP-Kernlogik
│   ├── database.php       # PDO/SQLite-Singleton mit Hilfsmethoden
│   ├── auth.php           # Session-Auth + TOTP 2FA
│   ├── functions.php      # XSS-Schutz e(), CSRF, sendSMTPEmail()
│   ├── admin.php          # Adminbereich-Controller (~800 Zeilen)
│   ├── frontend.php       # Öffentliche Routen
│   ├── pdf_generator.php  # Antrags-PDF (FPDF)
│   ├── bescheid_generator.php  # Förderbescheid-PDF
│   └── receipt_generator.php   # Zuwendungsbestätigung-PDF
│
├── templates/             # PHP-HTML-Templates
│   ├── admin/             # Admin-Oberfläche
│   └── frontend/          # Öffentliche Seiten
│
├── api/
│   └── handler.php        # REST-API-Endpunkte
│
├── cron/
│   └── run.php            # Cron-Job für E-Mail-Automatisierung
│
├── assets/                # CSS, JS, Bilder
│
├── data/                  # Datenbankdatei + Uploads (Schreibrecht nötig!)
│   ├── database.sqlite    # SQLite-Datenbank (nach Setup erstellt)
│   ├── logo.png / logo.jpg
│   └── unterschrift_1.png … unterschrift_5.png
│
└── config/
    └── config.php         # Auto-generiert durch Setup (NICHT einchecken!)

Dateirechte setzen

Die Verzeichnisse config/ und data/ müssen vom Webserver beschreibbar sein. Setzen Sie die Berechtigungen nach dem Upload:

# Verzeichnisse für Webserver-Benutzer schreibbar machen
chmod 755 config/ data/

# Falls nötig: Besitzer auf Webserver-User setzen (z.B. www-data bei Apache/Ubuntu)
chown -R www-data:www-data config/ data/

# Alternativ mit ACL (empfohlen auf Shared Hosting)
setfacl -R -m u:www-data:rwx config/ data/
⚠️
Wichtig: Setzen Sie niemals 777-Berechtigungen auf Produktivsystemen. Das Verzeichnis data/ enthält die gesamte Datenbank und sollte nicht öffentlich zugänglich sein. Stellen Sie sicher, dass Ihr Webserver-Block direkten Zugriff auf /data/ und /config/ verbietet.

Setup-Assistent (setup.php)

Der einmalige Setup-Assistent initialisiert die SQLite-Datenbank und legt den ersten Administrator-Account an. Rufen Sie nach dem Upload die folgende URL im Browser auf:

https://ihre-domain.de/setup.php
1
Systemprüfung

Der Assistent prüft automatisch alle PHP-Extensions und Dateirechte. Fehlende Voraussetzungen werden rot markiert — beheben Sie diese vor dem Fortfahren.

2
Datenbankinitialisierung

Das System erstellt die SQLite-Datei unter data/database.sqlite und legt alle Tabellen mit korrektem Schema an. Dieser Vorgang dauert wenige Sekunden.

3
Administrator-Account erstellen

Geben Sie Benutzername, E-Mail-Adresse und ein sicheres Passwort (min. 12 Zeichen, Groß-/Kleinbuchstaben, Ziffern) für den ersten Admin-Account ein.

4
Basiseinstellungen

Tragen Sie den Namen Ihrer Organisation (site_name), das Kürzel (site_abbrev) und die E-Mail-Adresse für ausgehende Mails ein.

5
Setup abschließen & absichern

Nach erfolgreichem Abschluss wird setup.php automatisch umbenannt oder gesperrt. Stellen Sie zusätzlich sicher, dass die Datei auf dem Server nicht mehr erreichbar ist (z.B. via .htaccess deny).

🚫
Sicherheitshinweis: Löschen oder sperren Sie setup.php unmittelbar nach der Ersteinrichtung. Eine erneut aufrufbare Setup-Seite auf einem Produktivsystem ist ein kritisches Sicherheitsrisiko.

TOTP 2FA einrichten

Beim ersten Login nach dem Setup wird der Administrator automatisch zur Einrichtung der Zwei-Faktor-Authentifizierung (2FA) weitergeleitet. Das System verwendet TOTP (Time-based One-Time Password) nach RFC 6238 – kompatibel mit Google Authenticator, Microsoft Authenticator und allen gängigen TOTP-Apps.

1
Authentifizierungs-App installieren

Installieren Sie Google Authenticator (iOS/Android) oder Microsoft Authenticator auf Ihrem Smartphone. Alternativ funktioniert auch Authy oder jede andere TOTP-kompatible App.

2
QR-Code scannen

Das Portal zeigt einen QR-Code an. Öffnen Sie Ihre Authenticator-App, tippen Sie auf „+" oder „Konto hinzufügen" und scannen Sie den QR-Code. Alternativ können Sie den angezeigten geheimen Schlüssel manuell eingeben.

3
6-stelligen Code bestätigen

Geben Sie den aktuell in der App angezeigten 6-stelligen Code ein, um die Einrichtung zu bestätigen. Der Code wechselt alle 30 Sekunden. Das System akzeptiert Codes aus einem Zeitfenster von ±30 Sekunden für Uhrabweichungen.

4
Backup-Codes sichern

Notieren Sie den angezeigten geheimen TOTP-Schlüssel an einem sicheren Ort. Bei Verlust des Smartphones ist dieser Schlüssel die einzige Möglichkeit, den Account ohne Datenbankzugriff wiederherzustellen.

ℹ️
Der TOTP-Geheimschlüssel wird verschlüsselt in der Spalte totp_secret der Tabelle users gespeichert. Bei Verlust kann ein Datenbankadministrator den Wert auf NULL setzen – beim nächsten Login wird dann ein neues TOTP eingerichtet.

Hosting-Konfiguration

Apache VirtualHost

Erstellen Sie eine neue VirtualHost-Konfiguration unter /etc/apache2/sites-available/foerderportal.conf:

<VirtualHost *:443>
    ServerName  portal.ihre-stiftung.de
    DocumentRoot /var/www/foerderportal

    <Directory /var/www/foerderportal>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Zugriff auf sensitive Verzeichnisse sperren
    <Directory /var/www/foerderportal/data>
        Require all denied
    </Directory>
    <Directory /var/www/foerderportal/config>
        Require all denied
    </Directory>

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/portal.ihre-stiftung.de/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/portal.ihre-stiftung.de/privkey.pem
</VirtualHost>

# HTTP → HTTPS Weiterleitung
<VirtualHost *:80>
    ServerName portal.ihre-stiftung.de
    Redirect permanent / https://portal.ihre-stiftung.de/
</VirtualHost>

Aktivieren Sie den VirtualHost und starten Sie Apache neu:

a2ensite foerderportal.conf
a2enmod rewrite ssl
systemctl restart apache2

Nginx Konfiguration

Binden Sie die mitgelieferte Datei nginx-security.conf in Ihren Server-Block ein:

server {
    listen 443 ssl;
    server_name portal.ihre-stiftung.de;
    root /var/www/foerderportal;
    index index.php;

    # Sicherheitskonfiguration einbinden
    include /var/www/foerderportal/nginx-security.conf;

    # URL-Rewriting für Single-Entry-Point
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Sensitive Verzeichnisse sperren
    location ~* ^/(data|config)/ {
        deny all;
        return 403;
    }

    # PHP-FPM
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    ssl_certificate     /etc/letsencrypt/live/portal.ihre-stiftung.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portal.ihre-stiftung.de/privkey.pem;
}

2 Einstellungen

Die Einstellungen sind im Adminbereich unter Einstellungen in sechs Tabs gegliedert. Alle Werte werden in der Tabelle settings als Schlüssel-Wert-Paare gespeichert und sofort wirksam.

E-Mail & SMTP konfigurieren

Das System versendet E-Mails über SMTP (empfohlen) oder als Fallback über die PHP-Funktion mail(). Konfigurieren Sie SMTP unter Einstellungen → E-Mail:

FeldBeispielwertBeschreibung
SMTP-Hostsmtp.strato.deHostname des ausgehenden Mailservers
SMTP-Port587587 (STARTTLS, empfohlen) oder 465 (SSL)
Verschlüsselungtlstls für STARTTLS, ssl für direktes SSL
SMTP-Benutzernameportal@stiftung.deMeist identisch mit der Absender-E-Mail
SMTP-Passwort••••••••Wird verschlüsselt in der Datenbank gespeichert
Absender-E-Mailportal@stiftung.deFrom-Adresse aller ausgehenden E-Mails
Absender-NameBürgerstiftung LohneAnzeigename im E-Mail-Client des Empfängers
Nutzen Sie die Schaltfläche „Test-E-Mail senden" nach dem Speichern, um die SMTP-Verbindung zu prüfen. Eine Testmail wird an die konfigurierte Admin-E-Mail-Adresse gesendet. Prüfen Sie auch den Spam-Ordner und konfigurieren Sie ggf. SPF/DKIM für Ihre Domain.

E-Mail-Vorlagen & Platzhalter

Unter Einstellungen → E-Mail-Vorlagen können Sie die automatisch versendeten E-Mails individuell anpassen. Jede Vorlage hat einen Betreff und einen HTML/Text-Körper. Folgende Platzhalter werden beim Versand durch die echten Werte ersetzt:

PlatzhalterErsetzt durchVerfügbar in
{{name}}Vor- und Nachname des AntragstellersAlle Vorlagen
{{vorname}}Nur VornameAlle Vorlagen
{{nachname}}Nur NachnameAlle Vorlagen
{{organisation}}Name der antragstellenden OrganisationAlle Vorlagen
{{projekt}}Kurzbeschreibung des ProjektsAntrags-Vorlagen
{{betrag}}Bewilligter Förderbetrag (formatiert, z.B. „2.500,00 €")Bewilligungs-Vorlage
{{antragsnummer}}Eindeutige Antrags-ID (z.B. DEMO-2025-001234)Alle Vorlagen
{{projektstart}}Geplanter Projektbeginn (formatiert)Antrags-Vorlagen
{{projektende}}Geplantes Projektende (formatiert)Antrags-Vorlagen
{{gesamtkosten}}Gesamtkosten des ProjektsAntrags-Vorlagen
{{foerderbetrag}}Beantragter FörderbetragEingangsbestätigung

Beispiel: Bewilligungsmail

Betreff: Ihr Förderantrag {{antragsnummer}} wurde bewilligt

Sehr geehrte/r {{name}},

wir freuen uns, Ihnen mitteilen zu können, dass Ihr Förderantrag
für das Projekt „{{projekt}}" in unserer Vorstandssitzung positiv
beschieden wurde.

Bewilligter Förderbetrag: {{betrag}}
Antragsnummer: {{antragsnummer}}
Projektzeitraum: {{projektstart}} bis {{projektende}}

Den offiziellen Förderbescheid finden Sie im Anhang dieser E-Mail.

Mit freundlichen Grüßen
Ihr Team der {{organisation}}

Automatisierung & Cron-Jobs

Das System sendet automatische Erinnerungs-E-Mails vor Projektbeginn und nach Projektende. Diese Funktion erfordert einen eingerichteten Cron-Job.

Option 1: Server-Crontab (empfohlen)

# Crontab bearbeiten:
crontab -e

# Täglich um 08:00 Uhr ausführen:
0 8 * * * php /var/www/foerderportal/cron/run.php >> /var/log/foerderportal-cron.log 2>&1

Option 2: Web-Trigger (für Shared Hosting)

Falls kein SSH-Zugriff verfügbar ist, kann der Cron-Job über eine URL ausgelöst werden. Der Sicherheitsschlüssel wird unter Einstellungen → Automatisierung → Cron-Key angezeigt:

GET https://ihre-domain.de/index.php?page=cron&key=IHR_CRON_KEY
⚠️
Der Cron-Key ist ein SHA256-Hash und sollte wie ein Passwort behandelt werden. Er wird in den Einstellungen generiert und kann bei Bedarf neu generiert werden. Tragen Sie diesen Key niemals in öffentlich zugängliche Dokumente ein.

Option 3: Manuell über das Dashboard

Unter Dashboard → Automatisierung finden Sie einen Button zum manuellen Auslösen des Cron-Laufs. Dies ist nützlich zum Testen und für ad-hoc Benachrichtigungen.

Dokumente-Tab

Im Reiter Dokumente konfigurieren Sie die Texte und Signaturen für automatisch generierte PDFs:

Förderbescheid-Texte

Passen Sie den Einleitungstext, die Bewilligungsformulierung, Bedingungen und die Schlussformel des Förderbescheids individuell an. Diese Texte werden 1:1 im PDF ausgegeben — Zeilenumbrüche werden berücksichtigt.

Unterschriften hochladen

Das System unterstützt bis zu 5 Unterschriften für Förderbescheide und Zuwendungsbestätigungen. Hochgeladene Bilder werden unter data/unterschrift_1.png bis data/unterschrift_5.png gespeichert.

  • Empfohlenes Format: PNG mit transparentem Hintergrund, 300 DPI
  • Empfohlene Größe: ca. 400 × 150 Pixel
  • Maximale Dateigröße: 2 MB pro Unterschrift
  • Zugelassene Formate: PNG, JPG

Integration & API

Unter Einstellungen → Integration verwalten Sie die Schnittstellen zu externen Diensten.

API-Zugangsdaten

Generieren Sie unter diesem Tab einen API-Key und API-Secret für die Nutzung der REST-Schnittstelle. Diese Zugangsdaten werden ausschließlich als HTTP-Header übertragen:

X-API-Key: ihr_api_key
X-API-Secret: ihr_api_secret
🚫
Sicherheitsfixhinweis: API-Zugangsdaten dürfen ausschließlich als HTTP-Header übertragen werden. Akzeptieren Sie keine API-Keys als URL-Parameter (?api_key=...) — diese Version des Systems hat diese Sicherheitslücke bereits geschlossen.

Rate Limiting

Die API ist auf 60 Anfragen pro Minute pro IP-Adresse begrenzt. Bei Überschreitung antwortet die API mit HTTP 429 (Too Many Requests). Zähler werden in der Tabelle rate_limit gespeichert und minütlich zurückgesetzt.

Design & Branding

Unter Einstellungen → Design passen Sie das Erscheinungsbild des Portals an Ihre Organisation an. Alle Änderungen werden als CSS-Variablen zur Laufzeit injiziert — kein Neustart erforderlich.

EinstellungSetting-KeyBeschreibung
Portal-Namesite_nameErsetzt „LBS FörderPortal" im gesamten System
Taglinesite_taglineUntertitel auf der öffentlichen Startseite
Kürzelsite_abbrevKurzes Kürzel für Antragsnummern (z.B. „LBS")
Primärfarbecolor_primaryHauptfarbe (CSS-Variable --primary)
Primärfarbe dunkelcolor_primary_darkDunklere Variante für Header etc.
Akzentfarbecolor_accentHighlights, Buttons, Badges
Footer-Textfooter_textHTML-Fußzeile (Impressum, Datenschutz etc.)
LogoUpload: wird als data/logo.png gespeichert
ℹ️
Das StiftOS-Branding-Toggle (Powered by StiftOS) ist an eine aktive Lizenz gebunden. Ohne aktivierten Lizenzschlüssel bleibt der Hinweis sichtbar. Tragen Sie Ihren StiftOS-Lizenzschlüssel unter Einstellungen → System ein, um das Branding vollständig zu entfernen.

System & Lizenz

Lizenz aktivieren

1
Lizenzschlüssel beziehen

Nach dem Lizenzkauf erhalten Sie per E-Mail einen Lizenzschlüssel im Format STIFTOS-XXXX-XXXX-XXXX-XXXX.

2
Schlüssel eintragen

Navigieren Sie zu Einstellungen → System → Lizenz und tragen Sie Ihren Schlüssel ein. Klicken Sie auf „Lizenz aktivieren".

3
Aktivierungsstatus

Das System kontaktiert den Lizenzserver und bestätigt die Aktivierung. Der Status wird täglich per Cron überprüft. Bei fehlgeschlagener Überprüfung werden bestimmte White-Label-Funktionen gesperrt.

Rollen & Rechte

👑 admin

  • Vollzugriff auf alle Funktionen
  • Systemkonfiguration & Einstellungen
  • Benutzerverwaltung
  • Lizenz-Management
  • Alle Berichte & Exporte

🏛️ vorstand

  • Förderanträge einsehen
  • Anträge bewilligen/ablehnen
  • Vorstandssitzungen verwalten
  • Bescheide erstellen
  • Keine Systemeinstellungen

💰 schatzmeister

  • Zahlungen erfassen & buchen
  • Spendenverwaltung
  • Zuwendungsbestätigungen
  • Finanzberichte
  • Keine Beschlüsse

👁️ beirat

  • Nur-Lese-Zugriff
  • Anträge einsehen
  • Berichte lesen
  • Keine Aktionen
  • Keine Einstellungen

3 Arbeitsprozesse

Förderanträge – Workflow

Der Lebenszyklus eines Förderantrags durchläuft definierte Status-Stufen. Der aktuelle Status bestimmt, welche Aktionen verfügbar sind und welche E-Mails automatisch versendet werden.

StatusAnzeigeBedeutungNächste Schritte
neu🔵 NeuAntrag eingegangen, noch nicht geprüftPrüfen, einer Sitzung zuweisen
in_bearbeitung🟡 In BearbeitungPrüfung läuft, ggf. RückfragenVorstandssitzung, Bescheid erstellen
bewilligt🟢 BewilligtFörderung beschlossen & Bescheid versandtZahlung buchen, Projektnachweise sammeln
abgelehnt🔴 AbgelehntAntrag abgelehnt, Bescheid versandtArchivierung
hinfällig⚫ HinfälligAntrag hat sich erledigt (z.B. Projektabsage)Archivierung
abgebrochen🟠 AbgebrochenVom Antragsteller zurückgezogenArchivierung

Typischer Workflow

1
Eingang (Status: neu)

Antrag wird über das öffentliche Formular oder die API importiert. Automatische Eingangsbestätigung per E-Mail. Admin erhält Benachrichtigung.

2
Prüfung (Status: in_bearbeitung)

Sachbearbeiter sichtet den Antrag, prüft Vollständigkeit und setzt Status auf „in Bearbeitung". Ggf. Rückfragen an Antragsteller per E-Mail aus dem System.

3
Vorstandssitzung

Antrag wird einer Vorstandssitzung zugewiesen. In der Sitzung wird abgestimmt. Ergebnis und Begründung werden im System dokumentiert.

4
Bescheid erstellen & versenden

Förderbescheid (PDF) wird automatisch generiert und per E-Mail an den Antragsteller versandt. Status wechselt zu „bewilligt" oder „abgelehnt".

5
Zahlung & Abschluss

Nach Projektbeginn wird die Zahlung durch den Schatzmeister gebucht. Der Cron-Job sendet automatische Erinnerungen vor Projektende für Nachweispflichten.

Digitale Signatur (OTP-Link)

Für bestimmte Fördertypen kann eine digitale Bestätigung durch den Antragsteller angefordert werden. Das System versendet einen signierten E-Mail-Link mit einem Einmal-OTP. Nach Klick und Bestätigung wird die Signatur mit Zeitstempel in der Datenbank gespeichert.

ℹ️
Die Antragsteller-Status-Abfrage (öffentliche Statusseite) verwendet ein separates, einmaliges E-Mail-OTP. Dieses ist unabhängig vom Admin-TOTP und erfordert keine Authenticator-App.

Vorstandssitzungen

Vorstandssitzungen bündeln mehrere Förderanträge für gemeinsame Beratung und Beschlussfassung.

1
Sitzung anlegen

Unter Sitzungen → Neue Sitzung tragen Sie Datum, Ort und ggf. eine Beschreibung ein.

2
Anträge zuweisen

Aus der Antragsliste können Anträge mit Status „neu" oder „in_bearbeitung" der Sitzung zugewiesen werden. Mehrfachauswahl möglich.

3
Protokoll & Abstimmung

Für jeden Antrag in der Sitzung wird das Abstimmungsergebnis (bewilligt/abgelehnt/vertagt) und optional eine Begründung erfasst.

4
Massenbescheide versenden

Nach Abschluss der Sitzung können alle Bescheide mit einem Klick generiert und per E-Mail versandt werden.

Spendenverwaltung

Das Spendenmodul verwaltet einmalige und wiederkehrende Spenden mit optionalem SEPA-Lastschriftmandat.

Spende erfassen

FeldPflichtfeldBeschreibung
SpendernameJaVor- und Nachname oder Organisation
E-Mail-AdresseFür QuittungEmpfänger der Zuwendungsbestätigung
BetragJaIn Euro, mit Centangabe (z.B. 150,00)
TurnusJaeinmalig, monatlich, vierteljaehrlich, jaehrlich
DatumJaDatum des Zahlungseingangs
ZweckNeinSpendenzweck für die Bestätigung
SEPA-MandatNeinMandatsnummer, IBAN, BIC für Lastschrift

Zahlungen buchen

Wiederkehrende Spenden erzeugen automatisch Zahlungserwartungen (in der Tabelle donation_payments). Der Schatzmeister bucht eingegangene Zahlungen als „bezahlt" und kann ausbleibende Zahlungen als „fehlgeschlagen" markieren.

Zuwendungsbestätigung (Zuwendungsbescheinigung)

Das System generiert steuerlich anerkannte Zuwendungsbestätigungen nach amtlichem Muster als PDF-Dokument.

1
Zuwendungsbestätigung öffnen

Wählen Sie eine Spende aus und klicken Sie auf „Zuwendungsbestätigung erstellen". Das System prüft, ob alle Pflichtfelder vorhanden sind.

2
PDF generieren

Das FPDF-Bibliothekssystem generiert das PDF mit amtlichem Muster, Vereinsdaten, Spenderbetrag und Unterschrift. Die Generierung dauert typischerweise <2 Sekunden.

3
Per E-Mail versenden

Das PDF wird als Anhang per SMTP an die hinterlegte Spender-E-Mail versandt. Eine Kopie landet im E-Mail-Log. Der Versand wird im Audit-Log protokolliert.

4
Download-Link

Ein Token-gesicherter Download-Link wird generiert. Der Spender kann die Bestätigung auch über das öffentliche Spendenportal herunterladen. Links sind via SHA256-Token gegen Manipulation geschützt.

⚠️
Steuerlicher Hinweis: Prüfen Sie, ob die vorausgefüllten Organisationsdaten (Steuernummer, Freistellungsbescheid-Datum, Verwendungszweck) in den Systemeinstellungen korrekt hinterlegt sind. Falsche Angaben können zur Unwirksamkeit der Zuwendungsbestätigung führen.

Statistiken & Berichte

Das Dashboard bietet interaktive Diagramme über Chart.js:

  • Antragsstatistik: Neue Anträge pro Monat, Status-Verteilung, Bewilligungsquote
  • Finanzübersicht: Bewilligte Förderbeträge nach Monat/Jahr, Gesamtvolumen
  • Spendenstatistik: Spendeneingang nach Turnus und Zeitraum, Neugewinnung vs. Bestandsspender
  • E-Mail-Status: Zustellquote, Fehler im E-Mail-Log

4 Technische Referenz

API-Referenz

Die REST-API ermöglicht die Integration mit externen Formularsystemen (FluentForms), Automatisierungsplattformen (z.B. n8n) und eigenen Anwendungen. Die Basis-URL lautet: https://ihre-domain.de/index.php

ℹ️
Alle authentifizierten Endpunkte erfordern die Header X-API-Key und X-API-Secret. URL-Parameter für Credentials werden nicht akzeptiert und führen zu einer 401-Antwort.
POST ?page=api&action=import Auth: API Key + Secret

Importiert einen neuen Förderantrag aus FluentForms oder einer anderen Quelle. Erstellt einen neuen Eintrag in der applications-Tabelle mit Status neu.

curl -X POST https://ihre-domain.de/index.php?page=api&action=import \
  -H "X-API-Key: ihr_api_key" \
  -H "X-API-Secret: ihr_api_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "input_organisation": "Sportverein Musterstadt e.V.",
    "first_name": "Maria",
    "last_name": "Muster",
    "email": "maria@sportverein.de",
    "address_line_1": "Sportplatzweg 1",
    "city": "Musterstadt",
    "zip": "12345",
    "description_projekt": "Neubau Jugendraum",
    "projektdauer": "2025-04-01",
    "projektdauer_1": "2025-09-30",
    "numeric_field_gesamtkosten": "15000",
    "numeric_field_foerderbetrag": "5000",
    "dropdown": "Ja",
    "input_text_kontoinhaber": "Sportverein Musterstadt e.V.",
    "input_mask_IBAN": "DE89370400440532013000",
    "input_mask_bic": "COBADEFFXXX",
    "input_text_institut": "Commerzbank"
  }'

Erfolgreiche Antwort (HTTP 201):

{
  "success": true,
  "id": 42,
  "antragsnummer": "DEMO-2025-000042",
  "message": "Antrag erfolgreich importiert"
}
GET ?page=api&action=status Auth: Öffentlich

Gibt den Status eines Antrags zurück. Wird vom öffentlichen Statusportal genutzt. Parameter: id (Antrags-ID) oder antragsnummer.

curl "https://ihre-domain.de/index.php?page=api&action=status&antragsnummer=DEMO-2025-000042"
GET ?page=api&action=applications Auth: API Key + Secret

Listet alle Förderanträge auf. Unterstützt optionale Filter-Parameter: status, year, limit, offset.

curl "https://ihre-domain.de/index.php?page=api&action=applications&status=bewilligt&year=2025" \
  -H "X-API-Key: ihr_api_key" \
  -H "X-API-Secret: ihr_api_secret"
POST ?page=api&action=webhook Auth: API Key + Secret

Empfängt Webhook-Benachrichtigungen für Statusupdates von externen Systemen (z.B. Zahlungsdienstleister). Der Payload enthält antragsnummer und event.

curl -X POST "https://ihre-domain.de/index.php?page=api&action=webhook" \
  -H "X-API-Key: ihr_api_key" \
  -H "X-API-Secret: ihr_api_secret" \
  -H "Content-Type: application/json" \
  -d '{"antragsnummer": "DEMO-2025-000042", "event": "payment_confirmed", "amount": 5000}'

Externe Formular- & Webhook-Integration

Die empfohlene Integrationsarchitektur: FluentForms (WordPress) → Automatisierungsplattform (z.B. n8n) (Webhook/HTTP Request Node) → StiftOS API.

1
FluentForms-Formular anlegen

Erstellen Sie in FluentForms ein Formular mit allen benötigten Feldern. Verwenden Sie die unten aufgeführten Feldnamen für korrektes Mapping.

2
Webhook in FluentForms konfigurieren

Unter Formular → Integrationen → Webhook tragen Sie die Webhook-URL Ihrer Automatisierungsplattform ein. Aktivieren Sie „JSON payload" für alle Felder.

3
Automatisierungs-Workflow einrichten

In Ihrem Automatisierungs-Workflow: Webhook-Trigger-Node empfängt die Daten → HTTP-Request-Schritt Ihrer Automatisierungsplattform mappt und sendet die Felder an POST ?page=api&action=import mit den korrekten Headern.

4
Feld-Mapping konfigurieren

Stellen Sie in Ihrem HTTP-Client sicher, dass die FluentForms-Felder korrekt auf die API-Parameter gemappt sind (siehe Tabelle unten).

FluentForms FeldnameAPI ParameterPflicht
input_organisationinput_organisationJa
names.first_namefirst_nameJa
names.last_namelast_nameJa
address_1.address_line_1address_line_1Ja
address_1.citycityJa
address_1.zipzipJa
description_projektdescription_projektJa
projektdauerprojektdauer (Start)Ja
projektdauer_1projektdauer_1 (Ende)Ja
numeric_field_gesamtkostennumeric_field_gesamtkostenJa
numeric_field_foerderbetragnumeric_field_foerderbetragJa
dropdowndropdown (Bankkonto: Ja/Nein)Nein
input_text_kontoinhaberinput_text_kontoinhaberNein
input_mask_IBANinput_mask_IBANNein
input_mask_bicinput_mask_bicNein
input_text_institutinput_text_institutNein
(manuell hinzufügen)emailEmpfohlen

Cron-Job Referenz

Das Cron-Skript cron/run.php übernimmt folgende Aufgaben:

  • Erinnerungs-E-Mails vor Projektbeginn (konfigurierbare Tage vorab)
  • Erinnerungs-E-Mails nach Projektende (für Nachweispflichten)
  • Lizenz-Revalidierung (tägliche Lizenzprüfung)
  • Rate-Limit-Tabelle aufräumen (alte Einträge löschen)
# Manuelle Ausführung (mit Ausgabe)
php /var/www/foerderportal/cron/run.php

# Crontab-Eintrag: täglich 08:00 Uhr, Ausgabe in Logdatei
0 8 * * * php /var/www/foerderportal/cron/run.php >> /var/log/foerderportal.log 2>&1

# Web-Trigger mit Sicherheitsschlüssel (für Shared Hosting)
# GET https://ihre-domain.de/index.php?page=cron&key=SHA256_KEY

PDF-System (FPDF)

Das System nutzt die FPDF-Bibliothek für die PDF-Generierung. FPDF wird beim ersten Bedarf automatisch von fpdf.org heruntergeladen und mit einem SHA-256-Prüfwert verifiziert. Kein manueller Download erforderlich.

PDF-TypGenerator-DateiRoute
Förderantragincludes/pdf_generator.php?page=pdf&action=antrag&token=SHA256
Förderbescheidincludes/bescheid_generator.php?page=pdf&action=bescheid&token=SHA256
Zuwendungsbestätigungincludes/receipt_generator.php?page=pdf&action=zuwendung&token=SHA256

Download-Links werden mit hash_equals() für timing-sicheren SHA256-Token-Vergleich verifiziert. Dies verhindert Timing-Angriffe bei der Token-Validierung.

Falls die automatische FPDF-Installation scheitert (z.B. wegen Firewall), laden Sie FPDF manuell von fpdf.org herunter und platzieren Sie die Datei unter includes/fpdf/fpdf.php. Prüfen Sie den SHA-256-Hash in pdf_generator.php.

Datenbankstruktur

Die SQLite-Datenbank liegt unter data/database.sqlite. Das Schema wird beim Setup automatisch erstellt — es gibt kein separates Migrations-Tooling.

TabellePrimärschlüsselZweckWichtige Spalten
applicationsid Kernentität: Förderanträge antragsnummer, status, email, foerderbetrag, created_at
usersid Admin-Accounts & Rollen username, email, role, totp_secret, password_hash
meetingsid Vorstandssitzungen title, meeting_date, status, notes
donationsid Spendenstammdaten donor_name, amount, turnus, sepa_mandate
donation_paymentsid Einzelne Zahlungsbuchungen donation_id, amount, due_date, paid_at, status
email_logid Alle versendeten E-Mails recipient, subject, status, error_message, sent_at
audit_logid Sicherheits- & Aktions-Protokoll user_id, action, details, ip_address, created_at
settingskey Systemkonfiguration (Key-Value) key, value
rate_limitid IP-basiertes Rate-Limiting ip_address, endpoint, requests, window_start

Sicherheitsarchitektur

MechanismusImplementierungBeschreibung
XSS-Schutze() in functions.phpAlle Nutzerausgaben über htmlspecialchars() geleitet. Niemals rohes echo in Templates.
CSRF-SchutzverifyCSRFToken()Alle POST-Formulare enthalten ein CSRF-Token, das serverseitig validiert wird.
SQL InjectionPDO Prepared StatementsAlle Datenbankabfragen ausnahmslos über Parameter-Binding. Kein String-Concatenation in SQL.
TOTP 2FARFC 6238 TOTPPflicht für alle Admin-Logins. Kompatibel mit Google/Microsoft Authenticator.
Rate Limitingrate_limit-Tabelle60 Anfragen/Minute pro IP für API-Endpunkte.
Token-Validierunghash_equals()Timing-sicherer Vergleich bei PDF-Download-Token-Prüfung.
API-CredentialsHTTP-Header onlyAPI-Key & Secret ausschließlich via X-API-Key/X-API-Secret Header.
Redirect-SicherheitWhitelist-ValidierungWeiterleitungsziele aus sicheren Konstanten, nie aus $_SERVER['REQUEST_URI'].
⚠️
Bekannte Einschränkung: Die Hilfsmethoden insert(), update() und delete() in database.php interpolieren Tabellennamen als String. Übergeben Sie daher ausschließlich hartcodierte Tabellennamen-Strings — niemals Benutzereingaben als Tabellennamen.

Backup & Wiederherstellung

Das gesamte System lässt sich mit zwei Pfaden vollständig sichern:

# Backup: SQLite-Datenbank + Uploads sichern
DATUM=$(date +%Y%m%d_%H%M%S)
cp /var/www/foerderportal/data/database.sqlite /backup/foerderportal-db-$DATUM.sqlite
tar -czf /backup/foerderportal-data-$DATUM.tar.gz /var/www/foerderportal/data/

# Vollständiges Backup (inkl. Code)
tar -czf /backup/foerderportal-full-$DATUM.tar.gz /var/www/foerderportal/

# Wiederherstellung
cp /backup/foerderportal-db-20250101_080000.sqlite /var/www/foerderportal/data/database.sqlite
chown www-data:www-data /var/www/foerderportal/data/database.sqlite
Richten Sie einen täglichen Backup-Cron ein und testen Sie die Wiederherstellung regelmäßig. SQLite erlaubt das einfache Kopieren der Datei während des Betriebs — für konsistente Backups verwenden Sie sqlite3 database.sqlite ".backup /backup/backup.sqlite".

Troubleshooting & FAQ

1. Setup.php zeigt „PDO SQLite Extension nicht gefunden"

Ursache: Die PHP-Extension pdo_sqlite ist nicht geladen.
Lösung: Führen Sie php -m | grep pdo_sqlite aus. Falls leer, installieren Sie die Extension: apt install php8.3-sqlite3 (Ubuntu) oder aktivieren Sie extension=pdo_sqlite in der php.ini.

2. E-Mails werden nicht versandt / SMTP-Fehler

Ursache: Falsche SMTP-Einstellungen, Firewall blockiert Port 587, oder SSL-Zertifikat-Problem.
Lösung: Prüfen Sie die Einstellungen über Test-E-Mail senden. Schauen Sie in den E-Mail-Log unter Protokolle → E-Mail für genaue Fehlermeldungen. Stellen Sie sicher, dass Ihr Server ausgehende Verbindungen auf Port 587/465 erlaubt.

3. PDF-Generierung schlägt fehl (Blank Page / Error)

Ursache: FPDF konnte nicht heruntergeladen werden, oder Schreibrecht auf temp-Verzeichnis fehlt.
Lösung: Prüfen Sie, ob allow_url_fopen = On in der php.ini gesetzt ist. Laden Sie FPDF manuell herunter und platzieren Sie es unter includes/fpdf/fpdf.php. Prüfen Sie sys_get_temp_dir() auf Schreibbarkeit.

4. TOTP-Code wird nicht akzeptiert

Ursache: Serverzeit weicht stark von der Smartphone-Zeit ab (TOTP ist zeitbasiert).
Lösung: Synchronisieren Sie die Serverzeit: ntpdate -u pool.ntp.org oder aktivieren Sie systemd-timesyncd. Abweichungen bis ±30 Sekunden werden toleriert.

5. API antwortet mit „401 Unauthorized"

Ursache: API-Key oder Secret fehlt, oder wurde als URL-Parameter statt Header übergeben.
Lösung: Stellen Sie sicher, dass X-API-Key und X-API-Secret als HTTP-Request-Header gesetzt sind. URL-Parameter werden aus Sicherheitsgründen nicht akzeptiert.

6. „403 Forbidden" beim Zugriff auf /data/ oder /config/

Ursache: Diese Verzeichnisse sind bewusst gesperrt — das ist korrekt.
Lösung: Der direkte Webzugriff auf diese Verzeichnisse ist aus Sicherheitsgründen geblockt. Greifen Sie auf die Daten ausschließlich über die Anwendung zu.

7. Cron-Job sendet keine E-Mails

Ursache: PHP-Pfad in der Crontab falsch, oder fehlende Berechtigungen.
Lösung: Testen Sie manuell: php /var/www/foerderportal/cron/run.php. Prüfen Sie den PHP-Pfad mit which php. Führen Sie den Cron-Job als denselben User aus wie den Webserver (z.B. www-data).

8. Datenbank wächst sehr schnell

Ursache: Der E-Mail-Log und Audit-Log akkumulieren über Zeit viele Einträge.
Lösung: Führen Sie regelmäßig sqlite3 data/database.sqlite "DELETE FROM email_log WHERE sent_at < datetime('now', '-365 days')" aus. Komprimieren Sie danach mit VACUUM.

9. „Weiße Seite" nach Update

Ursache: PHP-Fehler bei Syntaxfehler in aktualisierten Dateien, oder fehlende Extension.
Lösung: Aktivieren Sie temporär Fehlerausgabe: display_errors = On in php.ini. Prüfen Sie das PHP-Error-Log (error_log-Pfad in phpinfo). Testen Sie Syntax: php -l includes/admin.php.

10. Lizenzaktivierung schlägt fehl

Ursache: Keine Internetverbindung vom Server, falscher Lizenzschlüssel, oder Lizenz bereits auf anderem Server aktiviert.
Lösung: Prüfen Sie, ob der Server ausgehende HTTPS-Anfragen stellen kann: . Kontaktieren Sie den Support mit Ihrer Bestellnummer, wenn die Lizenz auf einem anderen Server deaktiviert werden soll.

11. Förderantrag aus FluentForms wird nicht importiert

Ursache: Mapping in der Automatisierungsplattform falsch konfiguriert, oder falsche Content-Type-Header.
Lösung: Aktivieren Sie das Execution-Log in Ihrer Automatisierungsplattform (z.B. n8n) und prüfen Sie den genauen Request-Body. Stellen Sie sicher, dass Content-Type: application/json gesetzt ist. Testen Sie mit dem curl-Beispiel aus dieser Dokumentation.

12. .htaccess Fehler / 500 Internal Server Error

Ursache: Apache-Modul mod_rewrite ist nicht aktiviert, oder AllowOverride All fehlt.
Lösung: Aktivieren Sie mod_rewrite: a2enmod rewrite && systemctl restart apache2. Stellen Sie in der VirtualHost-Konfiguration AllowOverride All für das Dokumenten-Verzeichnis sicher.

StiftOS FörderPortal – Offizielles Benutzerhandbuch v1.0 Stand: März 2026 · Alle Angaben ohne Gewähr ← Zurück zu stiftos.de