Praxisanleitung zur API-Anbindung, Fehlerbehandlung, SVRL-Auswertung und PDF-Visualisierung.
Der Validierungsdienst speichert keine Rechnungsdaten; sie werden ausschließlich im flüchtigen RAM-Arbeitsspeicher verarbeitet. Die Nicht-Speicherung der Rechnungsdaten wird vertraglich zugesichert.
Mit dem folgenden Muster senden Sie eine XML- oder PDF/A-Rechnung aus FileMaker an den Validierungsdienst, werten die Antwort aus und speichern die erzeugte Rechnungsansicht als PDF in einem Containerfeld. Passen Sie die Feld- und Tabellennamen an Ihre eigene Lösung an.
Alle Codebeispiele verwenden englische FileMaker-Scriptschritte und -Optionen. Tabellenauftreten, Feldnamen und sichtbare Dialogtexte bleiben lösungsspezifisch.
<API-ENDPUNKT> durch die mitgeteilte Adresse
und veröffentlichen Sie weder Endpunkt noch Zugangsdaten.
Empfohlen werden folgende Felder:
| Feld | Typ | Verwendung |
|---|---|---|
Rechnung::Originaldatei | Container | XML- oder PDF-Eingabedatei |
Rechnung::Visualisierung | Container | zurückgeliefertes PDF |
Rechnung::API_Antwort | Text | vollständige JSON-Antwort für Diagnose und Nachvollziehbarkeit |
Rechnung::Validierungen | Text | Liste der ausgeführten Prüfungen |
Rechnung::Pruefberichte | Text | SVRL-Berichte oder daraus abgeleitete Meldungen |
Rechnung::StrukturierteDaten | Text | optionaler Inhalt aus content |
Rechnung::Validierungsstatus | Text | eigener zusammengefasster Status |
Bewahren Sie den API-Schlüssel in einer geschützten Einstellungstabelle oder einer geeigneten Geheimnisverwaltung auf. Er gehört nicht in ein ungeschütztes globales Feld, einen Script-Kommentar oder ein Fehlerprotokoll.
Eine erfolgreiche Antwort kann beispielsweise so aufgebaut sein:
{
"validation": [
"<AUSGEFUEHRTES-XSD>",
"<AUSGEFUEHRTE-REGELPRUEFUNG>"
],
"reports": [
"<svrl:schematron-output>...</svrl:schematron-output>"
],
"format": "factur-x",
"profile": "en16931",
"pdf": "<BASE64-KODIERTES-PDF>",
"content": {}
}
Nicht jedes Feld ist in jeder Antwort vorhanden:
| Element | Datentyp | Bedeutung |
|---|---|---|
validation |
Array | Namen der technisch ausgeführten XSD- und Regelprüfungen. Ein Schematron-Name bestätigt die Ausführung, aber noch nicht die fachliche Fehlerfreiheit. |
reports |
Array | XML-Prüfberichte als Strings im SVRL-Format. svrl:failed-assert kennzeichnet eine verletzte Regel; location, id, flag und svrl:text beschreiben Fundstelle, Regel, Schweregrad und Meldung. |
format |
Text | Erkanntes Rechnungsformat, soweit es eindeutig bestimmt werden konnte, beispielsweise factur-x. |
profile |
Text | Erkanntes Factur-X-Profil, beispielsweise minimum, basicwl, basic, en16931 oder extended. |
pdf |
Text | Erzeugte PDF-Visualisierung als Base64-kodierter Text. Das Feld kann fehlen, wenn das Format nicht visualisiert wurde oder eine Transformation fehlgeschlagen ist. |
content |
Objekt | Aus der Rechnung abgeleitete strukturierte Daten. Aufbau und Pfade hängen vom Format ab; die Daten ersetzen nicht die Originalrechnung. |
detail |
Text oder Objekt | Fehlerdetail, insbesondere bei HTTP-Fehlern wie 400, 403 oder 500. |
Message |
Text | Allgemeine Verarbeitungsmeldung des Dienstes. |
error |
Text oder Objekt | Fehlertext oder umfangreiche Fehlerstruktur. Speichern Sie im Fehlerfall deshalb immer die vollständige Antwort. |
validation und der Auswertung der SVRL-Berichte in reports. Ein leeres
reports-Array bedeutet allein weder „gültig“ noch „ungültig“.
Verwenden Sie format und profile nur für Folgeentscheidungen, wenn die
Elemente tatsächlich vorhanden sind. UBL Invoice, UBL CreditNote und CII-basierte Formate können
in content unterschiedliche Strukturen und Pfade zurückgeben.
Set Error Capture [ On ]
Allow User Abort [ Off ]
If [ IsEmpty ( Rechnung::Originaldatei ) ]
Show Custom Dialog [ "Keine Rechnung" ;
"Bitte wählen Sie zuerst eine XML- oder PDF-Datei aus." ]
Exit Script [ Text Result: "Eingabedatei fehlt" ]
End If
Set Variable [ $request ; Value:
JSONSetElement (
"{}" ;
[ "data" ;
Base64EncodeRFC ( 4648 ; Rechnung::Originaldatei ) ;
JSONString ]
)
]
Set Variable [ $url ; Value: "<API-ENDPUNKT>" ]
Set Variable [ $apiKey ; Value: Einstellungen::API_Key ]
Base64EncodeRFC ( 4648 ; … ) erzeugt Base64 ohne zusätzliche Zeilenumbrüche.
Ein Container mit der unveränderten Originaldatei verhindert unbemerkte Änderungen an
Zeichensatz oder Zeilenenden.
Set Variable [ $curlOptions ; Value:
"--request POST" & ¶ &
"--header " & Quote ( "Content-Type: application/json" ) & ¶ &
If (
not IsEmpty ( $apiKey ) ;
"--header " & Quote ( "x-api-key: " & $apiKey ) & ¶ ;
""
) &
"--data @$request" & ¶ &
"--dump-header $responseHeaders" & ¶ &
"--connect-timeout 15" & ¶ &
"--max-time 120" & ¶ &
"--show-error"
]
Senden Sie den Header x-api-key nur, wenn tatsächlich ein Schlüssel vorhanden
ist. --fail wird nicht verwendet, damit auch bei HTTP 400, 403 oder 500 der
JSON-Fehlertext des Servers in FileMaker ankommt.
Set Variable [ $response ; Value: "" ]
Set Variable [ $responseHeaders ; Value: "" ]
Insert from URL [
Select ;
With dialog: Off ;
Target: $response ;
$url ;
Verify SSL Certificates ;
cURL options: $curlOptions
]
Set Variable [ $lastError ; Value:
JSONSetElement ( "{}" ;
[ "code" ; Get ( LastError ) ; JSONNumber ] ;
[ "detail" ; Get ( LastErrorDetail ) ; JSONString ]
)
]
Set Variable [ $fmError ; Value: JSONGetElement ( $lastError ; "code" ) ]
Set Variable [ $fmErrorDetail ; Value: JSONGetElement ( $lastError ; "detail" ) ]
Set Field [ Rechnung::API_Antwort ; $response ]
Beide Fehlerwerte werden innerhalb desselben ersten Set Variable-Schritts nach
Insert from URL ausgewertet. Aktivieren Sie produktiv immer
Verify SSL Certificates und verwenden Sie nicht --insecure.
Set Variable [ $headerLines ; Value:
Substitute ( $responseHeaders ; Char ( 13 ) ; "" )
]
Set Variable [ $headerIndex ; Value: ValueCount ( $headerLines ) ]
Set Variable [ $httpStatusLine ; Value: "" ]
Loop [ Flush: Always ]
Exit Loop If [ $headerIndex < 1 ]
Set Variable [ $httpStatusLine ; Value:
GetValue ( $headerLines ; $headerIndex )
]
Exit Loop If [ Left ( $httpStatusLine ; 5 ) = "HTTP/" ]
Set Variable [ $headerIndex ; Value: $headerIndex - 1 ]
End Loop
Set Variable [ $httpStatus ; Value:
If ( Left ( $httpStatusLine ; 5 ) = "HTTP/" ;
GetAsNumber ( MiddleWords ( $httpStatusLine ; 2 ; 1 ) ) ; 0 )
]
If [ $fmError ≠ 0 and $httpStatus = 0 ]
Set Variable [ $result ; Value:
JSONSetElement ( "{}" ;
[ "status" ; "transport_error" ; JSONString ] ;
[ "filemaker_error" ; $fmError ; JSONNumber ] ;
[ "detail" ; $fmErrorDetail ; JSONString ]
)
]
Exit Script [ Text Result: $result ]
End If
Bei Weiterleitungen können mehrere Statuszeilen vorkommen. Maßgeblich ist die letzte mit
HTTP/ beginnende Zeile. Status 0 bedeutet, dass keine HTTP-Antwort
ausgewertet werden konnte.
Set Variable [ $formatted ; Value: JSONFormatElements ( $response ) ]
If [ Left ( $formatted ; 1 ) = "?" ]
Exit Script [ Text Result:
JSONSetElement ( "{}" ;
[ "status" ; "invalid_response" ; JSONString ] ;
[ "http_status" ; $httpStatus ; JSONNumber ] ;
[ "response" ; $response ; JSONString ]
)
]
End If
If [ JSONGetElementType ( $response ; "" ) ≠ JSONObject ]
Exit Script [ Text Result: "Die API-Antwort ist kein JSON-Objekt." ]
End If
Set Variable [ $responseKeys ; Value: JSONListKeys ( $response ; "" ) ]
Set Variable [ $detail ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "detail" ) ) ;
JSONGetElement ( $response ; "detail" ) ; "" )
]
Set Variable [ $message ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "Message" ) ) ;
JSONGetElement ( $response ; "Message" ) ; "" )
]
Set Variable [ $error ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "error" ) ) ;
JSONGetElement ( $response ; "error" ) ; "" )
]
If [ $httpStatus < 200 or $httpStatus ≥ 300 ]
Exit Script [ Text Result:
JSONSetElement ( "{}" ;
[ "status" ; "http_error" ; JSONString ] ;
[ "http_status" ; $httpStatus ; JSONNumber ] ;
[ "detail" ; $detail ; JSONString ] ;
[ "message" ; $message ; JSONString ] ;
[ "error" ; $error ; JSONString ]
)
]
End If
Prüfen Sie in dieser Reihenfolge: FileMaker-/cURL-Fehler, HTTP-Status aus
$responseHeaders, gültiges JSON, die API-Felder detail,
Message und error sowie zuletzt die fachliche Validierung.
Set Variable [ $validations ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "validation" ) ) ;
JSONListValues ( $response ; "validation" ) ; "" )
]
Set Field [ Rechnung::Validierungen ; $validations ]
Ein Schematron-Name bestätigt nur, dass die Regelprüfung ausgeführt wurde. Ob fachliche Fehler
vorliegen, ergibt sich aus den SVRL-Berichten in reports.
Set Variable [ $reportCount ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "reports" ) ) ;
ValueCount ( JSONListKeys ( $response ; "reports" ) ) ; 0 )
]
Set Variable [ $i ; Value: 0 ]
Set Variable [ $failedAssertCount ; Value: 0 ]
Loop [ Flush: Always ]
Exit Loop If [ $i ≥ $reportCount ]
Set Variable [ $report ; Value:
JSONGetElement ( $response ; "reports[" & $i & "]" )
]
Set Variable [ $failedAssertCount ; Value:
$failedAssertCount +
PatternCount ( $report ; "<svrl:failed-assert" )
]
Set Variable [ $i ; Value: $i + 1 ]
End Loop
Set Field [ Rechnung::Pruefberichte ;
If ( $reportCount > 0 ;
JSONFormatElements ( JSONGetElement ( $response ; "reports" ) ) ; "" )
]
Die Textzählung eignet sich für eine einfache Ja/Nein-Anzeige. Benötigen Sie Regel-ID, Schweregrad, XPath und Meldung einzeln, verarbeiten Sie das SVRL mit einem XML-Parser.
Set Variable [ $pdfBase64 ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "pdf" ) ) ;
JSONGetElement ( $response ; "pdf" ) ; "" )
]
If [ not IsEmpty ( $pdfBase64 ) ]
Set Variable [ $pdfContainer ; Value:
Base64Decode (
$pdfBase64 ;
"Rechnungsvisualisierung.pdf"
)
]
Set Field [ Rechnung::Visualisierung ; $pdfContainer ]
End If
Der Dateiname im zweiten Parameter von Base64Decode ist wichtig, damit FileMaker
das Ergebnis als PDF-Containerdaten und nicht als Text behandelt.
Set Variable [ $content ; Value:
If ( not IsEmpty ( FilterValues ( $responseKeys ; "content" ) ) ;
JSONGetElement ( $response ; "content" ) ; "" )
]
If [ not IsEmpty ( $content ) ]
Set Field [ Rechnung::StrukturierteDaten ;
JSONFormatElements ( $content )
]
End If
content ist eine abgeleitete Darstellung und kein verlustfreier Ersatz für die
Originalrechnung. Bewahren Sie die ursprüngliche XML- oder PDF-Datei auf.
Die Antwort kann validation, reports, format,
profile, pdf und content enthalten. Fehler werden vor allem
über detail, Message oder error gemeldet. Eine HTTP-Antwort
mit Status 200 bedeutet deshalb nicht automatisch, dass die Rechnung fachlich gültig ist.
| Status | Bedeutung |
|---|---|
transport_error | FileMaker konnte den Dienst nicht ordnungsgemäß erreichen. |
http_error | Der Dienst antwortete beispielsweise mit 400, 403 oder 500. |
processing_error | Die Antwort enthält detail, Message oder relevante Daten in error. |
validation_incomplete | Die erwartete XSD-Prüfung fehlt oder es gibt keinen auswertbaren Regelbericht. |
validation_failed | Mindestens ein nicht akzeptierter failed-assert wurde gefunden. |
valid | Die erwartete XSD-Prüfung ist vorhanden und die relevanten Berichte enthalten keine nicht akzeptierten failed-assert-Einträge. |
| Beobachtung | Wahrscheinliche Ursache | Maßnahme |
|---|---|---|
| FileMaker-Fehler 1631 | Verbindung, DNS, TLS oder Timeout | Externe Fehlerdetails, Zertifikat und Erreichbarkeit prüfen |
| HTTP 400 | Request leer oder kein gültiges JSON | $request mit JSONFormatElements prüfen |
| HTTP 403 | Mitgesendeter API-Schlüssel ungültig | Schlüssel und Header prüfen; leeren Header nicht mitsenden |
| HTTP 500 | Unerwarteter Serverfehler | Vollständige Antwort und Zeitpunkt für den Support sichern |
failed-assert im Report | Schema technisch bestanden, Geschäftsregel verletzt | Regel-ID, XPath und Meldung auswerten |
pdf fehlt | Format nicht visualisierbar oder Transformation fehlgeschlagen | Transformationsdetails und vollständige Antwort prüfen |
| PDF im Container nicht verwendbar | Base64 als Text statt als Container dekodiert | Base64Decode mit Dateiname und .pdf verwenden |
--connect-timeout und --max-time.Testen Sie die Integration mindestens mit diesen Fällen:
factur-x.xml,Der Test ist bestanden, wenn die Lösung Transportfehler, HTTP-Fehler, fachliche Fehler und eine gültige Rechnung sichtbar voneinander unterscheidet.