Skip to content

Latest commit

 

History

History
266 lines (213 loc) · 12.2 KB

erp_notification_avs.adoc

File metadata and controls

266 lines (213 loc) · 12.2 KB

E-Rezept API-Dokumentation für Benachrichtigungen für Apotheken gematik logo

Zielgruppe: AVS E30615

Auf dieser Seite dokumentiert die gematik die Schnittstellen des E-Rezept-Fachdienstes für Apotheken, über welche sie sich für Benachrichtigungen bei neuen Communications, registrieren können.

Funktionsweise

Die Umsetzung der Notification-Schnittstelle des E-Rezept-Fachdienstes folgt den Vorgaben des FHIR-Standards. notification avs overview

Das AVS sendet eine Registrierungsanforderung an die VAU des E-Rezept-Fachdienstes, dieser generiert ein Pseudonym auf Basis der Telematik-ID und ein Bearer Token.

Mit diesem Bearer Token baut das AVS eine Websocket-Verbindung an der Subscription-Schnittstelle des Fachdienstes auf und erhält je neu vorliegender Communications-Ressource für die Telematik-ID ein Ping. Das Ping ist dann Trigger für das Abrufen der ungelesenen Communications.

notification avs sequence

Registrierung

Der Aufbau der WebSocket-Verbindung erfolgt zweistufig. Als erstes erfolgt die Authentisierung durch die VAU. Diese stellt ein eigenes Bearer-Token aus, das im zweiten Schritt beim Aufbau der eigentlichen Socketverbindung an den Subscription-Service übergeben wird.

Pseudonymgenerierung in der VAU (Authentisierung)

Zunächst muss für die Apotheke als authentisierter Client (gültiges AccessToken des IDP) ein Subscription-Request an die VAU gesendet werden.

Request

URI

/Subscription

Method

POST

Requester

AVS E30615

Responder

eRp—​FD blue

Request

POST /Subscription HTTP/1.1
Host: erp.zentral.erp.splitdns.ti-dienste.de
User-Agent: Avs/1.0 AvSoft/GEMAvwepokrpxnwiorlc
Cache-Control: no-cache
Authorization: Bearer eyJraWQ.ewogImL2pA10Qql22ddtutrvx4FsDlz.rHQjEmB1lLmpqn9J
Content-Type: application/fhir+xml; charset=UTF-8
Accept: application/fhir+xml; charset=utf-8

<Subscription xmlns="http://hl7.org/fhir">
    <id value="erp-notification-avs-01-request-PostSubscriptionPseudo"/>
    <status value="requested"/>
    <reason value="Communication notifications"/>
    <criteria value="Communication?received=null&amp;recipient=3-abc-12345678"/>
    <channel>
        <type value="websocket"/>
    </channel>
</Subscription>
ℹ️
Im http-Header des äußeren http-Requests an die VAU (POST /VAU) sind die Header X-erp-user: l und X-erp-resource: Subscription zu setzen.
ℹ️
Das Feld ` <criteria value=""` benennt die Suchparameter, bei denen eine Notification verschickt werden soll. Das sind zum einen received=null für ungelesene Nachrichten und recipient=3-abc-12345678 die Telematik-ID der Apotheke, die mit der Telematik-ID des IDP-AccessToken übereinstimmen muss. Andere Parameter werden aktuell nicht unterstützt. *ACHTUNG: das "&" muss als "&amp;" codiert werden.

Response

HTTP/1.1 200 OK
Content-Length: 510
Content-Type: application/fhir+xml;charset=utf-8

<Subscription xmlns="http://hl7.org/fhir">
    <id value="erp-notification-avs-02-response-PostSubscriptionPseudo"/>
    <status value="active"/>
    <end value="2025-01-15T15:29:00+00:00"/>
    <reason value="Communication notifications"/>
    <criteria value="Communication?received=null&amp;recipient=3-abc-12345678"/>
    <channel>
        <type value="websocket"/>
        <header value="Authorization: Bearer eyJhbGciOiAiYnJhaW5wb29sUDI1NnIxIiwidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTWF0aGlzIGJyYWlucG9vbCBqd3QiLCJpYXQiOiAxNjMyMjk0MzY1LCJleHAiOiAxNjYzODMwMzY1LCJhdWQiOiAibG9jYWxob3N0Iiwic3ViIjogIm15VXNlcm5hbWUiLCJzdWJzY3JpcHRpb25JZCI6ICIxMjNhYmMifQ.MEUCIAKqlB50xqNhnHkP6qoOoll33l3rWQ-_b5XfQJAUErnFAiEAlGR-cEl7DCzaoHqifh0drreFInsqo1xVy3SrWSMmNCI"/>
    </channel>
</Subscription>
ℹ️
In ` <id value="df694c098c2fb373524150461cfd9d23"/>` ist eine eindeutige ID (Pseudonym der Telematik-ID) hinterlegt
ℹ️
Der timestamp in <end value="2022-01-01T00:00:00Z"/> ` errechnet sich aus jetzt + 12h (UTC Timestamp)
In Klärung für RU: `jetzt + 1h
um den Verbinungsabbau nach Ablauf zu testen
ℹ️
die Header müssen in ` <header value="*"` beim Web Socket Upgrade mitgegeben werden

Die Schnittstelle antwortet mit den typischen http-StatusCodes des RESTful-Paradigmas

Code

Type Success

201

Created

Code

Type Error

400

Bad Request

401

Unauthorized

403

Forbidden

429

Too Many Requests

500

Server Errors

Websocket an Subscription-Endpunkt

Nach der Registrierung der Subscription wird eine WebSocket-Verbindung zum eigentlichen NotificationService aufgebaut.

Request

URI

subscription

Method

GET

Requester

AVS E30615

Responder

eRp—​FD blue

Request

GET /subscription HTTP/1.1
Host: subscription.zentral.erp.splitdns.ti-dienste.de
Authorization: Bearer eyJhbGciOiAiYnJhaW5wb29sUDI1NnIxIiwidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTWF0aGlzIGJyYWlucG9vbCBqd3QiLCJpYXQiOiAxNjMyMjk0MzY1LCJleHAiOiAxNjYzODMwMzY1LCJhdWQiOiAibG9jYWxob3N0Iiwic3ViIjogIm15VXNlcm5hbWUiLCJzdWJzY3JpcHRpb25JZCI6ICIxMjNhYmMifQ.MEUCIAKqlB50xqNhnHkP6qoOoll33l3rWQ-_b5XfQJAUErnFAiEAlGR-cEl7DCzaoHqifh0drreFInsqo1xVy3SrWSMmNCI
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: q4xkcO32u266gldTuKaSOw==
💡
Dieser Request ist NICHT zusätzlich VAU-verschlüsselt, sondern wird TLS-verschlüsselt an den Subscription-Endpunkt geschickt.
💡
Je nach eingesetztem Framework lautet die Zieladresse dann wss://subscription.zentral.erp.splitdns.ti-dienste.de:443
bzw. zum Test in der TI-Referenzumgebung RU = wss://subscription-ref.zentral.erp.splitdns.ti-dienste.de:443
ℹ️
In Authorization: wird das von der VAU generierte Bearer Token mit dem Pseudonym über die Telematik-ID übergeben.
ℹ️
In Sec-WebSocket-Key werden clientseitig generierte Nonce (16-Byte Zufallswert in base64-Codierung), siehe RFC-6455 Seite 18, Punkt 7

Response

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fA9dggdnMPU79lJgAE3W4TRnyDM=
ℹ️
Der Subscription-Service antwortet mit einem Connection: Upgrade
💡
Der Subscription-Service antwortet beim Schließen der Websocket-Verbindung mit den Status-Codes gemäß RFC-6455, bspw. mit Status 1000 wenn ein abgelaufenes Bearer Token übergeben wird.
Beendet der Service die WebSocket-Verbindung aufgrund eines (internen) Fehlers, liefert er einen http-Status [502 Bad Gateway, 504 Gateway Timeout].
🔥
Der Websocket-Client MUSS eine zufällig gewählte Pause zw. 5 - 60 Sekunden warten, bevor eine neue Websocket-Verbindung aufgebaut wird.

Regisitrierung der Subscription in der Websocket-Verbindung

Das AVS registriert sich für die Subscription aus dem vorherigen Schritt, in dem eine bind Text Nachricht über die Websocket-Verbindung an den Subscription-Service geschickt wird.

bind: df694c098c2fb373524150461cfd9d23
ℹ️
Im Value für bind befindet sich die Subscription.id

Der Subscription Service antwortet mit einer "bound" um die Einrichtung der Subscription zu bestätigen.

bound: df694c098c2fb373524150461cfd9d23
ℹ️
Subscription.id

Benachrichtigung

Ist eine neue Communication eingegangen, benachrichtigt der Subscription-Service das AVS, indem eine ping <Subscription.id> Text-Nachricht über die Websocket-Verbindung gesendet wird.

ping: df694c098c2fb373524150461cfd9d23
ℹ️
Subscription.id
⚠️
Hinweis: die Nachricht ping: df694c098c2fb373524150461cfd9d23 ist KEIN Ping der Ping/Pong Control Frames für das Aufrechterhalten der Verbindung (siehe https://datatracker.ietf.org/doc/html/rfc6455#section-5.5).

Empfängt das AVS nun ein ping: df694c098c2fb373524150461cfd9d23, liegt eine neue Nachricht vor, die über das VAU-Protokoll zum Abrufen neuer Nachrichten heruntergeladen werden kann. Über die Websockets werden selbst keine Nachrichten oder andere E-Rezept-bezogenen Daten verschickt.

Beispielhafte Implementierung für Primärsysteme

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

class Program {
    static void Main() {
        //subscriptionId und bearertoken aus VAU-Request /Subcription extrahieren
        CreateSocket("df694c098c2fb373524150461cfd9d23",
            "Bearer eyJhbGciOiJFUzI1NiJ9.CnsKInN1YnNjcmlwdGlvbklkIjogImRmNjk0YzA5OGMyZmIzNzM1MjQxNTA0NjFjZmQ5ZDI…");
    }

    private static void CreateSocket(string subscriptionId, string bearertoken) {
        var _websocketObj = new ClientWebSocket();
        _websocketObj.Options.SetRequestHeader("Authorization", bearertoken);
        //url RU: "wss://subscription-ref.zentral.erp.splitdns.ti-dienste.de" und PU: "wss://subscription.zentral.erp.splitdns.ti-dienste.de"
        _websocketObj.ConnectAsync(new Uri("wss://subscription-ref.zentral.erp.splitdns.ti-dienste.de/subscription"), CancellationToken.None)
            .Wait();

        if (_websocketObj.State != WebSocketState.Open) {
            throw new Exception("Websocket ist nicht geöffnet");
        }

        {
            var bind = $"bind: {subscriptionId}";
            _websocketObj.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(bind)), WebSocketMessageType.Text, true, CancellationToken.None)
                .Wait();
            Console.Out.WriteLine($"Websocket-Bind: {bind}");

            var buffer = new ArraySegment<byte>(new byte[2048]);
            WebSocketReceiveResult wsr = _websocketObj.ReceiveAsync(buffer, CancellationToken.None).Result;
            var res = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, wsr.Count);
            Console.Out.WriteLine($"Websocket-Bound: {res}");
        }

        while (true) {
            var buffer = new ArraySegment<byte>(new byte[2048]);
            WebSocketReceiveResult wsr = _websocketObj.ReceiveAsync(buffer, CancellationToken.None).Result;
            // ReSharper disable once AssignNullToNotNullAttribute
            var res = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, wsr.Count);
            if (wsr.Count > 0) {
                Console.Out.WriteLine($"Websocket-Empfangen: {res} ({wsr.Count} Bytes) -> es liegen neue Nachrichten bereit!");
            }
        }
    }
}

⇒ Wichtige Hinweise ⇐

🔥
Jede eingestellte Nachricht führt zu einem Ping, ggfs. im Millisekundenbereich, wenn viele Nachrichten an einen Empfänger gerichtet werden. In Abhängigkeit von der Implementierung kann dieses Verhalten zu einer Überlastung des PS führen, wenn bspw. jedes einzelne Ping den Anwendungsfall "Nachrichten von Versicherten empfangen" triggert.
Im Zweifel ist eine Wartezeit im AVS hilfreich, in der die zuletzt abgerufenen Nachrichten bearbeitet werden. Zwischenzeitlich "gepingte" Nachrichten gehen nicht verloren, da sie beim nächsten Abruf ungelesener Nachrichten gesammelt heruntergeladen werden.
🔥
Wird die WebSocket-Verbindung aufgrund eines Fehlers unerwartet terminiert, MUSS der Websocket-Client eine zufällig gewählte Pause zw. 5 - 60 Sekunden warten, bevor eine neue Websocket-Verbindung aufgebaut wird.
ℹ️
Je Telematik-ID ist nur ein Websocket möglich
ℹ️
Die Websocket-Verbindung wird nach 12h automatisch geschlossen und muss neu registriert werden.