CBAM, SUMA, RelayHub
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user