CBAM, SUMA, RelayHub

This commit is contained in:
2026-10-06 09:24:20 +02:00
parent 7be9708ede
commit 9eb8a1d800
13 changed files with 1786 additions and 382 deletions

View File

@@ -36,6 +36,7 @@ Public Class cRelayHub
Public Property contactPersonPhoneNumber As String
Public Property contactPersonPosition As String
Public Property contactPersonEmail As String ' <--- NEU
End Class
Public Class cRelayHubCost
@@ -45,11 +46,12 @@ Public Class cRelayHub
Public Class cRelayHubHeaderData
Public Property agentContact As cRelayHubAgentContact
Public Property declarantIsConsignee As Boolean
Public Property representationRelationshipCode As String ' <- war String
Public Property inputTaxDeduction As Boolean ' <- war String
' DAKOSY
Public Property declarantIsConsignee As Boolean?
Public Property representationRelationshipCode As String
Public Property inputTaxDeduction As Boolean?
Public Property procedureCodeRequested As String
' Entfernt: goodsStatus, costs (nicht im Schema an dieser Stelle)
Public Property transportMeansArrivalIdentity As String
Public Property transportMeansNationalityCode As String
Public Property previousAdministrativeReferenceType As String
@@ -59,6 +61,13 @@ Public Class cRelayHub
Public Property departureCountry As String
Public Property addressedCustomsOffice As String
Public Property dv1CostAllocation As cRelayHubDv1CostAllocation
' MODALTRANS
Public Property serviceCode As String
Public Property docType As String
Public Property declarationType As String
Public Property declarantEori As String
Public Property additionalProcedureCode As String
End Class
Public Class cRelayHubAddress
@@ -70,6 +79,7 @@ Public Class cRelayHub
Public Property countryCode As String
Public Property postalCode As String
Public Property city As String
Public Property district As String
End Class
Public Class cRelayHubDeclaration
@@ -80,15 +90,42 @@ Public Class cRelayHub
Public Class cRelayHubAdditionalData
Public Property transaction As cRelayHubTransaction
Public Property micImportShipment As cRelayHubMicImportShipment
Public Property declaration As List(Of cRelayHubDeclaration)
End Class
Public Class cRelayHubMicImportShipment
Public Property company As String
Public Property plant As String
Public Property connectionId As String
Public Property partnerId As String
Public Property partyId As String
Public Property messageId As String
Public Property transactionId As String
Public Property dateTime As String
Public Property sequenceNum As Integer?
Public Property actionCode As String
Public Property shipmentId As String
Public Property shipmentPartial As String
Public Property shipmentVersion As Integer?
Public Property documentNumber As Integer?
Public Property shipmentType As String
Public Property customsDate As String
End Class
Public Class cRelayHubAdditionalSettings
Public Property combineOptions As List(Of String)
Public Property combineItemsPriorityDocumentType As String
Public Property disableOutbound As Boolean?
Public Property disableDocumentConverter As Boolean?
Public Property disableAiToCommon As Boolean?
Public Property disableValidation As Boolean?
Public Property targetLang As String
End Class
Public Class cRelayHubJobOrderRequest
<JsonIgnore>
Public Property environment As cRelayHubEnvironment = cRelayHubEnvironment.PROD
Public Property referenceNo As String
Public Property dispatchCountry As String
Public Property destinationCountry As String
@@ -100,13 +137,27 @@ Public Class cRelayHub
' Neu: Pflichtfeld lt. Fehlermeldung
Public Property outputApplication As String ' "test" | "dakosy/sftp/vera" | "evrim/excel" | "sec/import/integration"
' "modaltrans/customs/declaration"
End Class
Public Class cRelayHubOutputApplication
Public Const Dakosy As String = "dakosy/sftp/verg"
Public Const Modaltrans As String = "modaltrans/customs/declaration"
Public Const MIC_NL_IMPORT As String = "MIC_NL_IMPORT"
End Class
Public Enum cRelayHubEnvironment
TEST
PROD
End Enum
Public Class cRelayHubJobOrderResponse
Public Property id As String
Public Property status As String
Public Property createdAt As String
Public Property referenceNo As String
Public Property jobOrderProcessingId As String
Public Property jobOrderNo As String
End Class
Public Class cRelayHubApiResult
@@ -117,42 +168,61 @@ Public Class cRelayHub
Public Property Data As cRelayHubJobOrderResponse ' Nur bei 201
End Class
Private Shared ReadOnly ENVIRONMENT As cRelayHubEnvironment = cRelayHubEnvironment.PROD
Shared API_URL As String = "https://declaranthub.singlewindow.io/api/v1-0"
Private Shared Function GetApiUrl(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return "https://test-relayhub.singlewindow.io/api/v1-0"
Case cRelayHubEnvironment.PROD
Return "https://declaranthub.singlewindow.io/api/v1-0"
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Public Class cRelayHub_sendToRelayHub_JobOrderRequest
Shared API_URL As String = "https://declaranthub.singlewindow.io/api/v1-0"
' Low-level Sender: holt Access-Token aus cRelayHubToken und sendet JSON
Private Shared Function SendJobOrder(jsonPayload As String) As Chilkat.HttpResponse
Private Shared Function SendJobOrder(jsonPayload As String, environment As cRelayHubEnvironment) As Chilkat.HttpResponse
Dim http As New Chilkat.Http
http.SetRequestHeader("Accept", "application/json")
' *** Token aus der separaten Token-Klasse beziehen ***
Dim token As String = cRelayHubToken.GetAccessToken()
Dim token As String = cRelayHubToken.GetAccessToken(environment)
http.AuthToken = token
'http.AuthToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqdkkzQ3BfbTVTb3JpdUFmSEZkZzJWUGgxcDFNWHA3ZnpjNFhyVHpGWVc4In0.eyJleHAiOjE3NjEzMDE4NTQsImlhdCI6MTc2MTIxNTQ1NCwianRpIjoiNWY3MjdkZTUtMmQ2MS00MThkLWJhYWQtNmY5ODM0NDk2NjY2IiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi5zaW5nbGV3aW5kb3cuaW8vYXV0aC9yZWFsbXMvYWdzdyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI5OWMwOTFmMy01Mjg1LTRhNzUtODdjNC0zODJlNThlNTBmNGMiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZ3N3LWFkbWluIiwic2lkIjoiMzRjMDY4ZGMtNjY3My00NDk5LTgzODAtMjVkOGYyNjI1NGY4IiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vc2luZ2xld2luZG93LmlvIiwiaHR0cHM6Ly9hZ3N3LnNpbmdsZXdpbmRvdy5pbyIsImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hZ3N3Iiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZHVjdC1jYXRhbG9nLXNjcCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJEaHViLVByb2QgVXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImRodWJAdmVyYWcuYWciLCJnaXZlbl9uYW1lIjoiRGh1Yi1Qcm9kIiwiZmFtaWx5X25hbWUiOiJVc2VyIiwiZW1haWwiOiJkaHViQHZlcmFnLmFnIn0.TZ8GkXrL7SSz1CEOnLB2eAAuVcYu3wlNtnAF6Y3wt0lnMGyse5tsI0YYSfGZG_Ae8zVcPqHqVT4rhDOgIPooBkq71z35ppd6b-FUaS6hSy4cMWQaidsHZr3hkID8amVQcB2vz9Yg985h3MOCSIn86d0x9i0euew3wEpF8eEHSfk_Wqf8jeJbiXAPHfy92K4Xh8IIuh-GXUTyN-NmGM94_NhcQwb4e7YMXjPPuslUsjABEfOvT7v4-AnO5B3ZczX0h0UgafmfiIyXSaL4IzYZojoMJ0CRsHdAK-j4gzXqz4lW0A9KMmHGHn01Oftt9mXfrEmCj0XGWF9xvBuYhxMUAQ"
Console.WriteLine("Using Token =: " & http.AuthToken)
Console.WriteLine("API_URL =: " & API_URL)
Return http.PostJson2(API_URL & "/job-orders/init", "application/json", jsonPayload)
Dim apiUrl As String = GetApiUrl(environment)
Console.WriteLine("API_URL =: " & apiUrl)
Return http.PostJson2(apiUrl & "/job-orders/init", "application/json", jsonPayload)
End Function
' Public API: erstellt Job-Order mit 401-Retry
Public Shared Function getJSON(request As cRelayHubJobOrderRequest) As String
Dim result As New cRelayHubApiResult()
Try
Dim sanitized = Sanitize(request)
Dim settings As New JsonSerializerSettings With {
.NullValueHandling = NullValueHandling.Ignore,
.ContractResolver = New Newtonsoft.Json.Serialization.DefaultContractResolver With {
.NamingStrategy = New Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
}
}
Dim jsonPayload As String = JsonConvert.SerializeObject(sanitized, settings)
Console.WriteLine("JSON → " & jsonPayload)
Return JsonConvert.SerializeObject(request)
Dim settings As New JsonSerializerSettings With {
.NullValueHandling = NullValueHandling.Ignore,
.ContractResolver = New Newtonsoft.Json.Serialization.DefaultContractResolver With {
.NamingStrategy = New Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
}
}
Dim jsonPayload As String =
JsonConvert.SerializeObject(sanitized, settings)
Console.WriteLine("JSON → " & jsonPayload)
Return jsonPayload
Catch ex As Exception
VERAG_PROG_ALLGEMEIN.cErrorHandler.ERR(ex.Message, ex.StackTrace, System.Reflection.MethodInfo.GetCurrentMethod.Name)
VERAG_PROG_ALLGEMEIN.cErrorHandler.ERR(
ex.Message,
ex.StackTrace,
System.Reflection.MethodInfo.GetCurrentMethod.Name)
Return "Error"
End Try
End Function
@@ -178,7 +248,7 @@ Public Class cRelayHub
' 1. Versuch
Dim response As Chilkat.HttpResponse = SendJobOrder(jsonPayload)
Dim response As Chilkat.HttpResponse = SendJobOrder(jsonPayload, request.environment)
If response Is Nothing Then
Return New cRelayHubApiResult With {
.Success = False, .StatusCode = 0, .Message = "Verbindungsfehler",
@@ -273,11 +343,11 @@ Public Class cRelayHub
Case "Rechnung", "Konsi-Rechnung", "INVOICE" : fileType = "commercial_invoice"
Case "CMR" : fileType = "cmr"
Case "Ausfuhr", "EXPORT" : fileType = "tr_export_declaration"
Case "ATR", "A.TR", "ATR-EUR1", "EUR1" : fileType = "atr"
Case "ATR", "A.TR", "ATR-EUR1" : fileType = "atr"
Case "Vorpapier", "T1" : fileType = "transit_declaration"
Case "Ursprungszeugnis", "CoO" : fileType = "origin"
Case "Lieferschein" : fileType = "delivery_note"
Case "Packliste" : fileType = "packaging_list"
Case "Packliste" : fileType = "packing_list"
Case "Freihandelszone", "FTZ" : fileType = "ftz"
Case "EUR1", "EUR", "EUR.1" : fileType = "eur"
Case "GGED_D", "GGED-D" : fileType = "gged_p"
@@ -287,7 +357,47 @@ Public Class cRelayHub
End If
Return fileType
End Function
Private Shared Function Sanitize(req As cRelayHub.cRelayHubJobOrderRequest) As cRelayHub.cRelayHubJobOrderRequest
Private Shared Function IsModaltrans(req As cRelayHubJobOrderRequest) As Boolean
Return String.Equals(req.outputApplication, "modaltrans/customs/declaration", StringComparison.OrdinalIgnoreCase)
End Function
Private Shared Function IsMIC_NL_IMPORT(req As cRelayHubJobOrderRequest) As Boolean
Return String.Equals(req.outputApplication, cRelayHubOutputApplication.MIC_NL_IMPORT, StringComparison.OrdinalIgnoreCase)
End Function
Private Shared Sub SetMIC_NL_IMPORT(req As cRelayHubJobOrderRequest)
If req.environment = cRelayHubEnvironment.PROD Then Throw New ApplicationException("MIC NL Import ist derzeit nur im RelayHub-Testsystem verfügbar.")
req.outputApplication = "mic/import"
req.regimeType = "IM"
If req.additionalData Is Nothing Then req.additionalData = New cRelayHubAdditionalData
If req.additionalData.micImportShipment Is Nothing Then req.additionalData.micImportShipment = New cRelayHubMicImportShipment
Dim mic = req.additionalData.micImportShipment
mic.company = "NL"
mic.plant = "IN"
mic.connectionId = "MICWebService"
mic.actionCode = "DIF"
If String.IsNullOrWhiteSpace(mic.partnerId) Then mic.partnerId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.shipmentId) Then mic.shipmentId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.messageId) Then mic.messageId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.transactionId) Then mic.transactionId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.dateTime) Then mic.dateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
If String.IsNullOrWhiteSpace(mic.shipmentType) Then mic.shipmentType = "E"
If req.additionalSettings Is Nothing Then req.additionalSettings = New cRelayHubAdditionalSettings
req.additionalSettings.disableOutbound = False
End Sub
Private Shared Function Sanitize(req As cRelayHubJobOrderRequest) As cRelayHubJobOrderRequest
If req Is Nothing Then Throw New ApplicationException("RelayHub Request fehlt.")
Dim isMicNlImport As Boolean = IsMIC_NL_IMPORT(req)
If isMicNlImport Then SetMIC_NL_IMPORT(req)
req.outputApplication = If(req.outputApplication, "").Trim()
req.dispatchCountry = If(req.dispatchCountry, "").Trim().ToUpperInvariant()
req.destinationCountry = If(req.destinationCountry, "").Trim().ToUpperInvariant()
@@ -295,31 +405,41 @@ Public Class cRelayHub
If req.dispatchCountry.Length <> 2 Then Throw New ApplicationException("dispatchCountry ISO-2 erforderlich.")
If req.destinationCountry.Length <> 2 Then Throw New ApplicationException("destinationCountry ISO-2 erforderlich.")
' AdditionalSettings -> combineOptions: nur erlaubte Keys, distinct, lower-case
Dim allowed As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"description", "commodity_code", "country_of_origin", "unit_type"
}
If isMicNlImport Then
If req.additionalData Is Nothing OrElse req.additionalData.micImportShipment Is Nothing Then Throw New ApplicationException("micImportShipment für MIC NL Import erforderlich.")
If req.additionalSettings IsNot Nothing Then
Dim mic = req.additionalData.micImportShipment
mic.partnerId = If(mic.partnerId, "").Trim()
mic.partyId = If(mic.partyId, "").Trim()
mic.shipmentId = If(mic.shipmentId, "").Trim()
If req.additionalSettings.combineOptions IsNot Nothing Then
Dim cleaned = req.additionalSettings.combineOptions.
Where(Function(o) Not String.IsNullOrWhiteSpace(o)).
Select(Function(o) o.Trim().ToLowerInvariant()).
Where(Function(o) allowed.Contains(o)).
Distinct().
ToList()
If mic.partnerId = "" Then Throw New ApplicationException("MIC partnerId erforderlich.")
If mic.partyId = "" Then Throw New ApplicationException("MIC partyId erforderlich.")
If mic.shipmentId = "" Then Throw New ApplicationException("MIC shipmentId erforderlich.")
req.additionalSettings.combineOptions = cleaned
ElseIf IsModaltrans(req) Then
If req.additionalData IsNot Nothing Then
req.additionalData.transaction = Nothing
If req.additionalData.declaration IsNot Nothing Then
For Each declaration In req.additionalData.declaration
declaration.objectIdentification = Nothing
Next
End If
End If
If req.additionalSettings IsNot Nothing AndAlso req.additionalSettings.combineOptions Is Nothing Then req.additionalSettings.combineOptions = New List(Of String)
Else
Dim allowed As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"description",
"commodity_code",
"country_of_origin",
"unit_type"
}
req.additionalSettings.combineItemsPriorityDocumentType =
If(req.additionalSettings.combineItemsPriorityDocumentType, "").Trim()
If req.additionalSettings.combineItemsPriorityDocumentType = "" Then
req.additionalSettings.combineItemsPriorityDocumentType = Nothing
If req.additionalSettings IsNot Nothing Then
If req.additionalSettings.combineOptions IsNot Nothing Then req.additionalSettings.combineOptions = req.additionalSettings.combineOptions.Where(Function(o) Not String.IsNullOrWhiteSpace(o)).Select(Function(o) o.Trim().ToLowerInvariant()).Where(Function(o) allowed.Contains(o)).Distinct().ToList()
req.additionalSettings.combineItemsPriorityDocumentType = If(req.additionalSettings.combineItemsPriorityDocumentType, "").Trim()
If req.additionalSettings.combineItemsPriorityDocumentType = "" Then req.additionalSettings.combineItemsPriorityDocumentType = Nothing
End If
End If
Return req

View File

@@ -2,208 +2,254 @@
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Web
Imports System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel
Imports Newtonsoft.Json
' NuGet: Newtonsoft.Json (>= 13.x)
Imports sun.security.util
Imports VERAG_PROG_ALLGEMEIN.cRelayHub
Public Class cRelayHubToken
' ======= KONFIG =======
Private Shared ReadOnly TOKEN_ENDPOINT As String = "https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
' Private Shared ReadOnly TOKEN_ENDPOINT As String = "https://dev-kc.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
' curl --location 'https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token' \
'--header 'Content-Type: application/x-www-form-urlencoded' \
'--data-urlencode 'grant_type=password' \
'--data-urlencode 'password=Dhub1234*' \
'--data-urlencode 'username=dhub@verag.ag' \
'--data-urlencode 'client_id=agsw-admin'
Private Shared ReadOnly TOKEN_ENDPOINT_PROD As String = "https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
Private Shared ReadOnly TOKEN_ENDPOINT_TEST As String = "https://dev-kc.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token"
Private Shared ReadOnly CLIENT_ID As String = "agsw-admin"
' Gewünscht: Zugangsdaten in der Klasse definieren
Private Shared ReadOnly USERNAME As String = "dhub@verag.ag" '"andreas.test@test.com"
Private Shared ReadOnly PASSWORD As String = "Dhub1234*" '"Password.123"
Private Shared ReadOnly USERNAME_PROD As String = "dhub@verag.ag"
Private Shared ReadOnly PASSWORD_PROD As String = "Dhub1234*"
' Token-File pro Benutzer unter %AppData%
Private Shared ReadOnly StorePath As String =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RelayHub", "token.json")
Private Shared ReadOnly USERNAME_TEST As String = "andreas.test@test.com"
Private Shared ReadOnly PASSWORD_TEST As String = "Atez123."
' Sicherheitspuffer, bevor wir erneuern (Sekunden)
Private Const ExpirySkewSeconds As Integer = 60
' ======= ÖFFENTLICHE API =======
''' <summary>
''' Liefert einen gültigen Access Token (nie Leerstring).
''' </summary>
Public Shared Function GetAccessToken() As String
Dim store = LoadStore()
'======================================================================
' KONFIGURATION
'======================================================================
Private Shared Function GetTokenEndpoint(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return TOKEN_ENDPOINT_TEST
Case cRelayHubEnvironment.PROD
Return TOKEN_ENDPOINT_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetUsername(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return USERNAME_TEST
Case cRelayHubEnvironment.PROD
Return USERNAME_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetPassword(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return PASSWORD_TEST
Case cRelayHubEnvironment.PROD
Return PASSWORD_PROD
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Private Shared Function GetStorePath(environment As cRelayHubEnvironment) As String
Dim fileName As String = If(environment = cRelayHubEnvironment.TEST, "token_test.json", "token_prod.json")
Return Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "RelayHub", fileName)
End Function
'======================================================================
' ACCESS TOKEN
'======================================================================
Public Shared Function GetAccessToken(environment As cRelayHubEnvironment) As String
Dim store = LoadStore(environment)
' 1) Wenn wir einen (noch) gültigen Token haben
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.AccessToken) Then
If store.ExpiresAtUtc > DateTimeOffset.UtcNow.AddSeconds(ExpirySkewSeconds) Then
Return store.AccessToken
End If
If store.ExpiresAtUtc > DateTimeOffset.UtcNow.AddSeconds(ExpirySkewSeconds) Then Return store.AccessToken
End If
' 2) Versuche Refresh, falls vorhanden
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.RefreshToken) Then
store = TryRefresh(store.RefreshToken)
End If
If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.RefreshToken) Then store = TryRefresh(store.RefreshToken, environment)
' 3) Fallback: Password-Grant Login
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then
store = PasswordLogin()
End If
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then store = PasswordLogin(environment)
' Validierung
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then
Throw New ApplicationException("Konnte keinen gültigen Access Token erhalten (leer).")
End If
If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then Throw New ApplicationException("Konnte keinen gültigen Access Token erhalten (" & environment.ToString() & ").")
' Persistieren & zurück
SaveStore(store)
SaveStore(store, environment)
Return store.AccessToken
End Function
' ======= INTERNES =======
Private Shared Function PasswordLogin() As TokenStore
'======================================================================
' LOGIN
'======================================================================
Private Shared Function PasswordLogin(environment As cRelayHubEnvironment) As TokenStore
Dim form = New Dictionary(Of String, String) From {
{"grant_type", "password"},
{"username", USERNAME},
{"password", PASSWORD},
{"username", GetUsername(environment)},
{"password", GetPassword(environment)},
{"client_id", CLIENT_ID}
}
Dim resp = PostForm(form)
Dim token = ParseTokenResponse(resp)
Return token
Dim resp = PostForm(form, environment)
Return ParseTokenResponse(resp, environment)
End Function
Private Shared Function TryRefresh(refreshToken As String) As TokenStore
'======================================================================
' REFRESH
'======================================================================
Private Shared Function TryRefresh(refreshToken As String, environment As cRelayHubEnvironment) As TokenStore
Try
Dim form = New Dictionary(Of String, String) From {
{"grant_type", "refresh_token"},
{"refresh_token", refreshToken},
{"client_id", CLIENT_ID}
}
Dim resp = PostForm(form)
Dim token = ParseTokenResponse(resp)
' Nur speichern, wenn ein Access Token vorhanden ist
Dim resp = PostForm(form, environment)
Dim token = ParseTokenResponse(resp, environment)
If Not String.IsNullOrWhiteSpace(token.AccessToken) Then
SaveStore(token)
SaveStore(token, environment)
Return token
End If
Catch ex As Exception
' Ignorieren -> fällt auf PasswordLogin zurück
Console.WriteLine("Token Refresh fehlgeschlagen (" & environment.ToString() & "): " & ex.Message)
End Try
Return Nothing
End Function
Private Shared Function PostForm(formFields As Dictionary(Of String, String)) As String
'======================================================================
' HTTP
'======================================================================
Private Shared Function PostForm(formFields As Dictionary(Of String, String), environment As cRelayHubEnvironment) As String
Using client As New HttpClient()
Using content As New FormUrlEncodedContent(formFields)
Dim response = client.PostAsync(TOKEN_ENDPOINT, content).Result
Dim tokenEndpoint As String = GetTokenEndpoint(environment)
Dim username As String = GetUsername(environment)
Dim password As String = GetPassword(environment)
Console.WriteLine("")
Console.WriteLine("==================================================")
Console.WriteLine(" RELAYHUB TOKEN REQUEST")
Console.WriteLine("==================================================")
Console.WriteLine("Environment : " & environment.ToString())
Console.WriteLine("Token Endpoint : " & tokenEndpoint)
Console.WriteLine("Client ID : " & CLIENT_ID)
'Console.WriteLine("Username : " & username)
'Console.WriteLine("Password : " & password)
'Console.WriteLine("Password Länge : " & If(password Is Nothing, 0, password.Length))
'Console.WriteLine("Grant Type : " & If(formFields.ContainsKey("grant_type"), formFields("grant_type"), "<nicht gesetzt>"))
'Console.WriteLine("Token Store : " & GetStorePath(environment))
'Console.WriteLine("==================================================")
Dim response = client.PostAsync(tokenEndpoint, content).Result
Dim body = response.Content.ReadAsStringAsync().Result
If Not response.IsSuccessStatusCode Then
Throw New ApplicationException(
$"Token-Endpoint Fehler ({CInt(response.StatusCode)}): {body}")
End If
If Not response.IsSuccessStatusCode Then Throw New ApplicationException("Token-Endpoint Fehler (" & environment.ToString() & " / " & CInt(response.StatusCode) & "): " & body)
Return body
End Using
End Using
End Function
Private Shared Function ParseTokenResponse(json As String) As TokenStore
'======================================================================
' TOKEN RESPONSE
'======================================================================
Private Shared Function ParseTokenResponse(json As String, environment As cRelayHubEnvironment) As TokenStore
Dim r = JsonConvert.DeserializeObject(Of TokenResponse)(json)
If r Is Nothing OrElse String.IsNullOrWhiteSpace(r.access_token) Then
Throw New ApplicationException("Token-Antwort ungültig oder ohne access_token.")
End If
If r Is Nothing OrElse String.IsNullOrWhiteSpace(r.access_token) Then Throw New ApplicationException("Token-Antwort ungültig oder ohne access_token.")
Dim now = DateTimeOffset.UtcNow
Dim expiresIn = If(r.expires_in <= 0, 3600, r.expires_in) ' Fallback 1h
Dim expiresIn = If(r.expires_in <= 0, 3600, r.expires_in)
Dim store = New TokenStore With {
.AccessToken = r.access_token.Trim(),
.RefreshToken = If(r.refresh_token, String.Empty),
.ExpiresAtUtc = now.AddSeconds(expiresIn)
}
.AccessToken = r.access_token.Trim(),
.RefreshToken = If(r.refresh_token, String.Empty),
.ExpiresAtUtc = now.AddSeconds(expiresIn)
}
' === Konsolen-Ausgabe ===
Console.WriteLine("== Neuer Token erhalten ==")
Console.WriteLine("Access Token: " & store.AccessToken)
Console.WriteLine("Refresh Token: " & store.RefreshToken)
Console.WriteLine("== Neuer RelayHub Token ==")
Console.WriteLine("Environment: " & environment.ToString())
Console.WriteLine("Gültig bis UTC: " & store.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss"))
Return store
End Function
' ======= PERSISTENZ =======
Private Shared Function LoadStore() As TokenStore
'======================================================================
' TOKEN LADEN
'======================================================================
Private Shared Function LoadStore(environment As cRelayHubEnvironment) As TokenStore
Try
If File.Exists(StorePath) Then
Dim json = File.ReadAllText(StorePath, Encoding.UTF8)
Dim storePath As String = GetStorePath(environment)
If File.Exists(storePath) Then
Dim json = File.ReadAllText(storePath, Encoding.UTF8)
Dim s = JsonConvert.DeserializeObject(Of TokenStore)(json)
' Ausgabe in Konsole
If s IsNot Nothing Then
Console.WriteLine("== Token aus Datei geladen ==")
Console.WriteLine("Access Token: " & (If(String.IsNullOrWhiteSpace(s.AccessToken), "<leer>", s.AccessToken)))
Console.WriteLine("Refresh Token: " & (If(String.IsNullOrWhiteSpace(s.RefreshToken), "<leer>", s.RefreshToken)))
Console.WriteLine("Environment: " & environment.ToString())
Console.WriteLine("Gültig bis UTC: " & s.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss"))
End If
Return s
Else
' Datei existiert nicht -> Info ausgeben
Console.WriteLine("Keine Token-Datei vorhanden, neuer Login erforderlich.")
Console.WriteLine("Keine Token-Datei für " & environment.ToString() & " vorhanden.")
End If
Catch ex As Exception
Console.WriteLine("Fehler beim Laden der Token-Datei: " & ex.Message)
' Datei defekt? -> Ignorieren, neu holen
End Try
Return Nothing
End Function
Private Shared Sub SaveStore(store As TokenStore)
'======================================================================
' TOKEN SPEICHERN
'======================================================================
Private Shared Sub SaveStore(store As TokenStore, environment As cRelayHubEnvironment)
Try
Dim dir = Path.GetDirectoryName(StorePath)
Dim storePath As String = GetStorePath(environment)
Dim dir = Path.GetDirectoryName(storePath)
If Not Directory.Exists(dir) Then Directory.CreateDirectory(dir)
' Datei immer neu schreiben/überschreiben
Dim json = JsonConvert.SerializeObject(store, Formatting.Indented)
File.WriteAllText(StorePath, json, Encoding.UTF8)
File.WriteAllText(storePath, json, Encoding.UTF8)
Console.WriteLine("Token-Datei gespeichert: " & StorePath)
Console.WriteLine("Token-Datei gespeichert: " & storePath)
Catch ex As Exception
Console.WriteLine("Fehler beim Speichern der Token-Datei: " & ex.Message)
End Try
End Sub
Public Shared Sub DeleteStore()
'======================================================================
' TOKEN LÖSCHEN
'======================================================================
Private Shared Sub Delete_Store(Optional environment As cRelayHubEnvironment = cRelayHubEnvironment.PROD)
Try
If System.IO.File.Exists(StorePath) Then System.IO.File.Delete(StorePath)
Console.WriteLine("Token-Datei gelöscht: " & StorePath)
Dim storePath As String = GetStorePath(environment)
If File.Exists(storePath) Then File.Delete(storePath)
Console.WriteLine("Token-Datei gelöscht: " & storePath)
Catch ex As Exception
Console.WriteLine("Fehler beim Löschen der Token-Datei: " & ex.Message)
End Try
End Sub
Public Shared Sub DeleteAllStores()
Delete_Store(cRelayHubEnvironment.TEST)
Delete_Store(cRelayHubEnvironment.PROD)
End Sub
' ======= DTOs =======
'======================================================================
' DTOs
'======================================================================
Private Class TokenResponse
Public Property access_token As String
Public Property expires_in As Integer
@@ -211,7 +257,6 @@ Public Class cRelayHubToken
Public Property refresh_token As String
Public Property token_type As String
Public Property scope As String
' … weitere Felder bei Bedarf
End Class
Private Class TokenStore
@@ -220,4 +265,4 @@ Public Class cRelayHubToken
Public Property ExpiresAtUtc As DateTimeOffset
End Class
End Class
End Class

View File

@@ -126,6 +126,10 @@ Public Class cATEZ_Read_T1
End Function
Public Function ProcessPdf(pdfPath As String) As T1ProcessResult
Return ProcessPdfAsync(pdfPath).GetAwaiter().GetResult()
End Function
' ==========================
' PROCESS PDF