<?xml version="1.0" encoding="UTF-8"?>
<!--
  Ward ADT ingress channel (template) for Mirth Connect 4.4+.

  Bridges the hospital's MLLP ADT feed to Ward's signed HTTPS webhook. The interface engine owns MLLP
  framing and re-delivery; Ward's contract is a single POST authenticated by an HMAC over
  "{unixSeconds}.{rawHL7}".

  Before importing:
    1. Settings -> Configuration Map, add:
         WARD_WEBHOOK_URL     = https://<project-ref>.supabase.co/functions/v1/emr-inbound
         WARD_FACILITY_ID     = <facility uuid>
         WARD_WEBHOOK_SECRET  = <the webhook secret loaded into Ward's Vault>
    2. Adjust the source TCP Listener port to the hospital's ADT feed.
    3. Deploy, then send a test ADT A01 and confirm a 200 with an HL7 ACK (MSA|AA) in the response.

  The destination transformer below is the load-bearing, Ward-specific part: it must produce exactly
  the signature Ward verifies. The surrounding connector scaffolding is a starting point; tune queueing,
  reconnect, and TLS to the site's standards.
-->
<channel version="4.4.0">
    <id>ward-adt-ingress-template</id>
    <name>Ward ADT Ingress (template)</name>
    <description>Forwards inbound ADT to the Ward emr-inbound webhook with an HMAC signature.</description>
    <sourceConnector version="4.4.0">
        <name>MLLP ADT Listener</name>
        <properties class="com.mirth.connect.connectors.tcp.TcpReceiverProperties" version="4.4.0">
            <listenerConnectorProperties version="4.4.0">
                <host>0.0.0.0</host>
                <port>6661</port>
            </listenerConnectorProperties>
            <transmissionModeProperties class="com.mirth.connect.plugins.mllpmode.MLLPModeProperties">
                <startOfMessageBytes>0B</startOfMessageBytes>
                <endOfMessageBytes>1C0D</endOfMessageBytes>
            </transmissionModeProperties>
            <serverMode>true</serverMode>
            <respondOnNewConnection>0</respondOnNewConnection>
        </properties>
        <transformer version="4.4.0">
            <elements/>
            <inboundDataType>HL7V2</inboundDataType>
            <outboundDataType>HL7V2</outboundDataType>
        </transformer>
        <filter version="4.4.0">
            <elements/>
        </filter>
    </sourceConnector>
    <destinationConnectors>
        <connector version="4.4.0">
            <name>Ward Webhook</name>
            <properties class="com.mirth.connect.connectors.http.HttpDispatcherProperties" version="4.4.0">
                <host>${WARD_WEBHOOK_URL}</host>
                <method>POST</method>
                <useHeadersVariable>false</useHeadersVariable>
                <headers class="linked-hash-map">
                    <entry>
                        <string>Content-Type</string>
                        <list><string>text/plain; charset=UTF-8</string></list>
                    </entry>
                    <entry>
                        <string>X-Facility-ID</string>
                        <list><string>${WARD_FACILITY_ID}</string></list>
                    </entry>
                    <entry>
                        <string>X-Webhook-Timestamp</string>
                        <list><string>${wardTimestamp}</string></list>
                    </entry>
                    <entry>
                        <string>X-Webhook-Signature</string>
                        <list><string>${wardSignature}</string></list>
                    </entry>
                </headers>
                <content>${message.rawData}</content>
                <contentType>text/plain</contentType>
                <dataTypeBinary>false</dataTypeBinary>
                <charset>UTF-8</charset>
            </properties>
            <transformer version="4.4.0">
                <elements>
                    <com.mirth.connect.plugins.javascriptstep.JavaScriptStep version="4.4.0">
                        <name>Sign for Ward</name>
                        <sequenceNumber>0</sequenceNumber>
                        <enabled>true</enabled>
                        <script>
// Produce the HMAC Ward verifies: hex SHA-256 of "{unixSeconds}.{rawHL7}", keyed by the webhook secret.
var secret = configurationMap.get('WARD_WEBHOOK_SECRET');
// Integer unix seconds. Do NOT use String.valueOf(millis / 1000): Java double division renders
// scientific notation ("1.75E9"), which Ward rejects as a non-integer timestamp.
var timestamp = String(Math.floor(Date.now() / 1000));
var body = connectorMessage.getRawData();
var signingInput = timestamp + '.' + body;

var Mac = Packages.javax.crypto.Mac;
var SecretKeySpec = Packages.javax.crypto.spec.SecretKeySpec;
var mac = Mac.getInstance('HmacSHA256');
mac.init(new SecretKeySpec(new java.lang.String(secret).getBytes('UTF-8'), 'HmacSHA256'));
var digest = mac.doFinal(new java.lang.String(signingInput).getBytes('UTF-8'));

var hex = new java.lang.StringBuilder();
for (var i = 0; i &lt; digest.length; i++) {
    // (b &amp; 0xFF) | 0x100 -&gt; 0x1xx, toHexString -&gt; "1xx", drop the leading 1 for zero-padded hex.
    hex.append(java.lang.Integer.toHexString((digest[i] &amp; 0xFF) | 0x100).substring(1));
}

channelMap.put('wardTimestamp', timestamp);
channelMap.put('wardSignature', hex.toString());
                        </script>
                    </com.mirth.connect.plugins.javascriptstep.JavaScriptStep>
                </elements>
                <inboundDataType>HL7V2</inboundDataType>
                <outboundDataType>HL7V2</outboundDataType>
            </transformer>
            <filter version="4.4.0">
                <elements/>
            </filter>
            <!--
              Destination queue is OFF on purpose. The signature is computed once per message; a queued
              retry would replay a now-stale timestamp and be rejected as stale after 300s, jamming the
              channel. Let the upstream MLLP sender own re-delivery (a NAK re-sends, and Ward signs
              afresh). If you must queue here, sign per dispatch attempt instead.
            -->
            <queueConnectorProperties version="4.4.0">
                <queueEnabled>false</queueEnabled>
            </queueConnectorProperties>
        </connector>
    </destinationConnectors>
    <preprocessingScript>return message;</preprocessingScript>
    <postprocessingScript>return;</postprocessingScript>
    <enabled>true</enabled>
</channel>
