Skip to content

AS4 Peppol

Purpose

Peppol AS4 Messaging Support

This DSL is based on the open source library Phase4: https://github.com/phax/phase4

See the folder https://github.com/phax/phase4/tree/master/phase4-peppol-client/src/test/java/com/helger/phase4/peppol for different examples on how to send messages via the Peppol AS4 client.

Methods

Binding name: p6.as4peppol


Method: AutoCloseable buildContextConfiguration(Map<String, String> mp)

Phase4 is designed to gather configuration from properties files provided at runtime. This does not suite the platform6 execution architecture hence the introduction of the context configuration

Context configuration replaces the use of properties files with a Map[:] of configuration attributes defined at runtime.

It is therefore essential that all calls to the DSL are made within the configuration context and the context is closed at the end of execution.

This method returns an AutoClosable making the lifecycle of the context easy to control in Groovy:

p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {

    // Context aware DSL calls here...

}

To reduce the configuration required to build a context each time, many of the Phase4 required properties have been defaults so only overrides are required. The defaults are:

Key Default Value
phase4.wss4j.syncsecurity “true”
global.debug “false”
global.production “true”
global.nostartupinfo “true”
global.datapath P6_DATA + “/resources/as4”
phase4.dump.path P6_DATA + “/resources/as4/dump”
org.apache.wss4j.crypto.provider “org.apache.wss4j.common.crypto.Merlin”
org.apache.wss4j.crypto.merlin.keystore.type “pkcs12”
org.apache.wss4j.crypto.merlin.keystore.file P6_DATA + “/resources/as4/as4peppol-ap.p12”
org.apache.wss4j.crypto.merlin.keystore.password “peppol”
org.apache.wss4j.crypto.merlin.keystore.provider “BC”
org.apache.wss4j.crypto.merlin.keystore.alias “cert”
org.apache.wss4j.crypto.merlin.load.cacerts “false”
org.apache.wss4j.crypto.merlin.truststore.type “jks”
org.apache.wss4j.crypto.merlin.truststore.file P6_DATA + “/resources/as4/as4peppol-complete-truststore.jks”
org.apache.wss4j.crypto.merlin.truststore.password “peppol”
org.apache.wss4j.crypto.merlin.keystore.private.password” same as org.apache.wss4j.crypto.merlin.keystore.password

Method: HttpClientSettings buildHttpSettings(SSLContext ctx)

Create a Phase4 HttpClientSettings object from a given SSLContext. This allows the p6.securesocket DSL to be used to define the HttpClient attributes when POSTing AS4


Method: Phase4PeppolSender.Builder sendBuilder()

Create a new Builder for AS4 messages if the payload is present and the SBDH (openPEPPOL Business Message Envelope) should be created internally


Method: Phase4PeppolSender.SBDHBuilder sendSBDHBuilder()

Create a new Builder for AS4 messages if the SBDH payload is already present


Method: PeppolIdentifierFactory idFactory()

Simple wrapper over Phase4PeppolSender.IF to allow id generation


Method: Class<Phase4PeppolSender> sender = Phase4PeppolSender.class

Simple wrapper to reference the Phase4PeppolSender class


Method: IAS4ClientBuildMessageCallback simpleMessageSendLogger()

A simple implementation of IAS4ClientBuildMessageCallback to log message details during the send process


Method: Tuple3<Integer, byte[], Map<String, String>> receive(final Map<String, String> headers, final byte[] ba, final Closure<Void>... messageReceipt)

Receive an openPEPPOL AS4 Message. A byte array containing the message content and a separate list of MIME headers are supplied as a Map as parameters.

This method takes an optional callback method to use during the receipt processing:

  • Closure - messageReceipt: is called when an inbound openPEPPOL Message is identified. The parameters passed are: filePath Path of received message, messageId: Unique id of the received message

The method returns a Tuple3 containing the following:

  • Integer: the HTTP status code to response with (200 is good)
  • String: the response entity as a byte array (typically a receipt)
  • Map: map of headers to use in a response to this request

Method: void setPerformSBDHValueChecking(boolean performSBDHValueChecking)

Enable value checks when reading Peppol SBDH documents (inbound message validation) default: false


Method: void setPerformReceiverChecking(boolean performReceiverChecking)

Enable receiver consistency checks (inbound validation) default: false


Examples

HTTP openPEPPOL AS4 Send with Receipt checker

The following is an example of sending a UBL Invoice document (XML defined via script resource) to the Austrian Governments test endpoint:

import groovy.xml.DOMBuilder

import com.helger.peppol.sml.ESML
import com.helger.smpclient.peppol.SMPClientReadOnly
import com.helger.phase4.dump.AS4RawResponseConsumerWriteToFile

def xml = p6.resource.get( "UBLInvoice" )
def payloadElement = DOMBuilder.parse(new StringReader(xml)).getDocumentElement()

// Austrian Government id
def receiverID = p6.as4peppol.idFactory().createParticipantIdentifierWithDefaultScheme ( "9915:test" )

def configMap = [:]

configMap["global.debug"] = "true"
configMap["global.production"] = "false"
configMap["org.apache.wss4j.crypto.merlin.keystore.password"] = "password-in-here"

p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {

    def ctx = p6.securesocket.contextBuilder()
            .setType( SecureContext.BundleType.ONE_WAY_TRUST_ANY )
            .setProtocol( "TLSv1.2" )
            .build();

    def httpSettings = p6.as4peppol.buildHttpSettings( p6.securesocket.contextBuild( ctx ) )

    def result = p6.as4peppol.sendBuilder()
            .documentTypeID ( p6.as4peppol.idFactory().createDocumentTypeIdentifierWithDefaultScheme ("urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1") )
            .processID ( p6.as4peppol.idFactory().createProcessIdentifierWithDefaultScheme ("urn:fdc:peppol.eu:2017:poacc:billing:01:1.0") )
            .senderParticipantID ( p6.as4peppol.idFactory().createParticipantIdentifierWithDefaultScheme ("9915:phase4-test-sender") )
            .receiverParticipantID ( receiverID )
            .senderPartyID ( "YOUR_POP_ID_HERE" )
            .payload ( payloadElement )
            .smpClient ( new SMPClientReadOnly (p6.as4peppol.sender.URL_PROVIDER, receiverID, ESML.DIGIT_TEST) )
            .rawResponseConsumer ( new AS4RawResponseConsumerWriteToFile () )
            .validationConfiguration ( null )
            .buildMessageCallback ( p6.as4peppol.simpleMessageSendLogger() )
            .httpClientFactory ( httpSettings )
            .sendMessageAndCheckForReceipt ()

    println result
}

Receive HTTP POSTed AS4 openPEPPOL Message and Generate Receipt

  1. Route Deployment Script

rest('/public')
        .post('/as4')
        .to('p6cmb://scripts?platform6.request.action=execute&id=AS4PeppolInbound')
        .id('AS4PeppolInbound')
2. Route Executed Script (AS4PeppolInbound)

def configMap = [:]

configMap["global.debug"] = "true";
configMap["global.production"] = "false";
configMap["org.apache.wss4j.crypto.merlin.keystore.password"] = "password-in-here"

p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {

    def tpl = p6.as4peppol.receive(
            p6.pipeline.toStringMap(),
            p6.pipeline.getBytes('body'),
            { sbdFilePath, messageId -> println '++++++ RECEIVED openPEPPOL Message\n' + '\nPath: ' + sbdFilePath + '\nMessageID: ' + messageId}
    )

    // --- Build the Http POST response into the pipeline ---

    // Response code
    p6.pipeline.put('CamelHttpResponseCode', '' + tpl.first)
    // The response or error-message body
    p6.pipeline.put('body', tpl.second)
    // Additional headers - prefixed with 'p6rest.' to ensure they are part of the final HTTP response
    tpl.third.each { key, val ->
        p6.pipeline.put( 'p6rest.' + key, val )
    }

}

Logging, Debug & Testing

The configuration properties global.production and global.debug are used to switch between Test and Production certificate usage and control the generation of DEBUG logs

In Addition, the following line should be added to the log4j.properties file to enable DEBUG logging from the Phase4 library:

log4j.logger.com.helger.phase4=DEBUG