Migrating from phase4 peppol standalone - phax/phoss-ap GitHub Wiki
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-configAPCoreConfig— typed accessors for all configuration properties- Spring profile support —
application-{profile}.propertiesfiles 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_syncorhttp_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.modeandduplicate.detection.sbdh.mode. - Document verification — validates inbound/outbound documents against Peppol BIS rules. Enable with
verification.inbound.enabled=true. - Sentry error tracking — set
sentry.dsnto enable. See Configuration Properties. - MLS support — configure with
mls.type. See Peppol Specifics. - Grouped AS4 dumps — set
phase4.dump.mode=groupedto 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 forwardingphoss-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.