Skip to content

Migrating from phase4 peppol standalone

Philip Helger edited this page Apr 9, 2026 · 4 revisions

Migrating from phase4-peppol-standalone

This guide is for users who have a working phase4-peppol-standalone deployment and want to migrate to phoss-ap. It covers the key differences and the steps required.

Why migrate?

phase4-peppol-standalone is a lightweight template for getting started with Peppol AS4. It stores nothing in a database, has no retry logic, no forwarding, and requires you to implement inbound message handling yourself. phoss-ap is a production-ready AP that provides all of this out of the box:

Capability phase4-peppol-standalone phoss-ap
Inbound message handling Custom SPI with TODOs Complete: DB storage, forwarding, duplicate detection
Outbound sending Fire-and-forget Persistent queue with retry and circuit breaker
Data persistence File-based only PostgreSQL or MySQL with Flyway migrations
Document forwarding Not included HTTP POST, S3, SFTP
Duplicate detection Not included AS4 Message ID + SBDH Instance ID
MLS (Message Level Status) Not included Full support
Document verification Not included Pluggable SPI (Phorm integration out of the box)
Peppol Reporting Default in-memory backend Database-backed
Error monitoring Logging only Logging + Sentry integration
AS4 message dumping Direction-based only Direction-based or grouped by exchange

See Architecture Overview for the full picture of phoss-ap's components.

Prerequisites

Before starting the migration you need:

  • Java 21 (same as phase4-peppol-standalone)
  • PostgreSQL 14+ or MySQL 8.0+ (new requirement)
  • Maven 3.9+ (same)

See Running phoss AP for detailed build and deployment instructions.

Step-by-step migration

1. Set up the database

phoss-ap requires PostgreSQL or MySQL. Flyway creates the schema automatically on first startup. Make sure to use more secure usernames and passwords as provided in below example.

psql -U postgres -c "CREATE DATABASE \"phoss-ap\";"
psql -U postgres -c "CREATE USER peppol WITH PASSWORD 'peppol';"
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE \"phoss-ap\" TO peppol;"

See Database Design Notes for schema details.

2. Map your configuration

Most configuration properties carry over directly. The table below shows the mapping:

phase4-peppol-standalone phoss-ap Notes
peppol.stage peppol.stage Unchanged
peppol.seatid peppol.owner.seatid Renamed
peppol.owner.countrycode peppol.owner.countrycode Unchanged
phase4.endpoint.address phase4.endpoint.address Unchanged
phase4.api.requiredtoken phase4.api.requiredtoken Unchanged
phase4.dump.path phase4.dump.path Unchanged. Additionally phase4.dump.mode can be set to grouped
smp.url peppol.smp.url Renamed
peppol.reporting.senderid (derived from Seat ID) No longer a separate property; the SP ID is derived from peppol.owner.seatid
peppol.reporting.scheduled peppol.reporting.schedule.enabled Renamed
peppol.reporting.schedule.* peppol.reporting.schedule.* Unchanged
global.debug (removed) Debug mode is always off
global.production (removed) Production mode is always on
global.datapath global.datapath Unchanged
http.proxy.* http.proxy.* Unchanged
Keystore/truststore (org.apache.wss4j.crypto.merlin.*) Same Unchanged
SMP client truststore (smpclient.truststore.*) Same Unchanged

New properties that must be configured (see Configuration Properties for the full list):

# Database (required)
phossap.jdbc.database-type=postgresql
phossap.jdbc.driver=org.postgresql.Driver
phossap.jdbc.url=jdbc:postgresql://localhost:5432/phoss-ap
phossap.jdbc.user=peppol
phossap.jdbc.password=peppol
phossap.jdbc.schema=ap

# Document storage (required)
storage.inbound.path=/var/phoss-ap/inbound
storage.outbound.path=/var/phoss-ap/outbound

# Forwarding — choose one (required for inbound processing)
forwarding.mode=http_post_async
forwarding.http.endpoint=http://your-backend:8888/forwarding

3. Remove your custom SPI

In phase4-peppol-standalone you implemented IPhase4PeppolIncomingSBDHandlerSPI in a class like CustomPeppolIncomingSBDHandlerSPI and registered it in META-INF/services/. phoss-ap ships its own production implementation (Phase4InboundMessageProcessorSPI) that handles:

  • Database storage of the inbound transaction
  • Document payload persistence
  • Duplicate detection (AS4 + SBDH level)
  • Optional document verification
  • Forwarding to your backend system
  • Peppol Reporting item creation
  • MLS response generation

Action: Delete your CustomPeppolIncomingSBDHandlerSPI class and its META-INF/services/ registration. phoss-ap's SPI is registered automatically.

If you had custom business logic in your SPI (beyond the TODOs), the equivalent integration point in phoss-ap is the forwarding layer — configure it to POST inbound documents to your backend system. See Receiving Process for the full flow.

4. Remove your servlet configuration

In phase4-peppol-standalone the ServletConfig class (~280 lines) manually configures:

  • AS4 server initialization
  • Crypto factory setup
  • AS4 message dumping
  • SMP receiver checks
  • Peppol Reporting backend
  • CRL downloader

Action: Delete your ServletConfig class. phoss-ap handles all of this in APServletInit which is invoked automatically by the AS4ServletRegistration Spring bean.

5. Remove your configuration accessor

The standalone project has an APConfig class that reads configuration via AS4Configuration.getConfig(). phoss-ap uses its own configuration stack:

  • APConfigProvider — central config access backed by ph-config
  • APCoreConfig — typed accessors for all configuration properties
  • Spring profile support — application-{profile}.properties files are automatically loaded

Action: Delete your APConfig class. Use APCoreConfig static methods instead.

6. Update your sending API calls

The REST API paths have changed:

phase4-peppol-standalone phoss-ap Notes
POST /sendas4/{s}/{r}/{d}/{p}/{c} POST /api/outbound/submit/{s}/{r}/{d}/{p}/{c} Same path parameters. Response format changed (see below)
POST /sendsbdh POST /api/outbound/submit-sbd
POST /sendas4-facturx/{s}/{r}/{c} (use submit with payloadMimeType param) Factur-X is handled via the payloadMimeType query parameter
GET /phase4ping (use actuator health) GET /actuator/health
GET /create-tsr/{y}/{m} GET /api/reporting/create-tsr/{y}/{m} Moved under /api/reporting prefix
GET /create-eusr/{y}/{m} GET /api/reporting/create-eusr/{y}/{m} Moved under /api/reporting prefix
GET /do-peppol-reporting/{y}/{m} GET /api/reporting/do-peppol-reporting/{y}/{m} Moved under /api/reporting prefix

New APIs in phoss-ap:

Endpoint Purpose
GET /api/outbound/status/{sbdhInstanceID} Query outbound transaction status
GET /api/outbound/in-transmission List transactions currently being sent
GET /api/inbound/status/{sbdhInstanceID} Query inbound transaction status
GET /api/inbound/in-processing List transactions currently being processed
POST /api/inbound/report Inbound transaction report (forwarding status)
GET /api/mls/missing List transactions with missing MLS responses

See API Specification for full details.

Response format change: In phase4-peppol-standalone, sending is synchronous — the API blocks until the AS4 message is sent (or fails) and returns the result directly. In phoss-ap, sending is asynchronous — the API queues the message and returns immediately with status: queued and the sbdhInstanceID. Use the status API to poll for the result.

7. Configure forwarding

This is the main new concept. In phase4-peppol-standalone, your CustomPeppolIncomingSBDHandlerSPI was the integration point — you wrote custom code to handle each inbound document. In phoss-ap, the AP receives and stores the document, then forwards it to your backend system using a configurable transport.

Choose one of:

  • HTTP POST (http_post_sync or http_post_async) — posts the SBD to an HTTP endpoint
  • S3 (s3_link) — writes to an S3 bucket
  • SFTP (sftp) — uploads via SFTP
  • Filesystem (filesystem) — writes to a local directory

Example for HTTP POST:

forwarding.mode=http_post_async
forwarding.http.endpoint=http://your-backend:8888/receive

See Configuration Properties for all forwarding options.

8. Review new capabilities

Once migrated, consider enabling these features that were not available in phase4-peppol-standalone:

  • Retry with exponential backoff — automatic for both sending and forwarding. See Retry and Resilience Patterns.
  • Duplicate detection — rejects or flags duplicate messages. Configure with duplicate.detection.as4.mode and duplicate.detection.sbdh.mode.
  • Document verification — validates inbound/outbound documents against Peppol BIS rules. Enable with verification.inbound.enabled=true.
  • Sentry error tracking — set sentry.dsn to enable. See Configuration Properties.
  • MLS support — configure with mls.type. See Peppol Specifics.
  • Grouped AS4 dumps — set phase4.dump.mode=grouped to correlate request/response messages in the same directory.

9. Test

Use the built-in test modules:

  • phoss-ap-testbackend — a mock forwarding backend that accepts HTTP POST forwarding
  • phoss-ap-testsender — a command-line tool for sending test documents

Common issues

"Failed to init Peppol Reporting Backend Service": The Peppol Reporting tables are created by Flyway. Make sure the peppol.reporting.jdbc.* and peppol.report.jdbc.* properties point to a reachable database instance (PostgreSQL or MySQL) and the user has CREATE permissions.

"No Storage Inbound/Outbound Path provided": Set storage.inbound.path and storage.outbound.path in your configuration. These directories are created automatically but the parent path must exist.

"The configured Peppol Seat ID does not match the syntactical requirements": The Seat ID format is POP followed by exactly 6 digits (e.g. POP000306). In phase4-peppol-standalone, the validation was less strict.

API returns 401: All API endpoints require the X-Token header. Set phase4.api.requiredtoken in your configuration, same as in phase4-peppol-standalone.

Clone this wiki locally