Sending Process - phax/phoss-ap GitHub Wiki
Sending Process
Overview
The sending process handles outbound Peppol document transmission from the Sender Backend (within C2) through the AP to the remote receiving AP (C3).
Process Steps
- Receive document via API - The Sender Backend provides data via the REST API (raw document + metadata, raw document with auto-detected document type via DDD, pre-built SBD, or S3 reference + metadata). Optionally includes an MLS_TO override. When an S3 reference is provided, the AP fetches the document from the sender's S3 bucket before proceeding.
- Store document - The document is written to disk under
storage.outbound.path; size and SHA-256 hash are computed while streaming. - Optional verification - The document is validated via the pluggable verification SPI (
IOutboundDocumentVerifierSPI) before any database record is created. If verification fails, no transaction is created at all, theonOutboundVerificationRejectionnotification is triggered, an error is returned to the caller, and no sending attempt is made. - Persist transaction - The file path and metadata are persisted to the
outbound_transactiontable in the database inpendingstate. - AS4 sending via phase4 - The orchestrator performs the SMP lookup itself (NAPTR resolution + service metadata lookup, guarded by a per-SMP circuit breaker) and hands the resolved endpoint to phase4. phase4 then performs SBDH creation (if needed, including MLS_TO in SBDH extensions), AS4 message construction, signing, and transmission. The synchronous AS4 receipt is checked. If the SMP lookup shows that the receiver is not registered, the transaction is marked as
permanently_failedwithout any AS4 sending attempt. The SMP lookup results are cached in memory across all messages and all receivers (see the SMP Client Cache properties), and all SMP queries share one HTTP connection pool. - Record sending attempt - Insert a row into
outbound_sending_attemptwith the outcome (AS4 Message ID, Receipt Message ID, HTTP status code, or error details). Update the parentoutbound_transactionstatus accordingly. - On success: trigger outbound reporting - After a successful send, a Peppol Reporting item is created (via phase4's
createPeppolReportingItemAfterSending) and stored, and theonOutboundDocumentSentlifecycle event is fired. The API response only signals overall success when the reporting item was stored as well. - On failure: schedule retry - The transaction is marked for retry. The retry strategy (max attempts, exponential backoff) is defined via the
retry.sending.*configuration properties. - On permanent failure: notify - When max retries are exhausted, the generic notification interface is invoked.
- Receive MLS response (asynchronous) - At a later point, the remote C3 may send an MLS response back via AS4. The AP receives it, correlates it with the original outbound transaction via the SBDH Instance Identifier, and updates the MLS status (received_ap, received_ab, or received_re).
Sequence Diagram: Sending
sequenceDiagram
participant SB as Sender Backend
participant AP as AP API
participant DB as DB
participant P4 as phase4
participant RA as Remote AP (C3)
SB->>AP: 1. Submit doc (raw+meta, auto-detect, SBD, or S3 ref+meta, optional mlsTo)
AP->>AP: 2. Store document to disk
AP->>AP: 3. Verify doc (optional)
note over AP: If verification fails: notify, return error (no DB record)
AP->>DB: 4. Insert into outbound_transaction (pending)
DB-->>AP: stored OK
AP->>AP: 5. SMP lookup (cached)
note over AP: If receiver not registered: mark permanently_failed, no AS4 attempt
AP->>P4: 6. Trigger AS4 send
P4->>RA: AS4 UserMessage
RA-->>P4: AS4 Receipt
P4-->>AP: AS4 result (Receipt MsgID, HTTP status)
AP->>DB: 7. Insert into outbound_sending_attempt
AP->>DB: Update outbound_transaction status
opt On success
AP->>AP: 8. Create Peppol Reporting item
AP->>DB: Store reporting record
end
AP-->>SB: API response (Phase4PeppolSendingReport as JSON, HTTP 200 or 422)
Retry Flow (asynchronous, triggered by scheduler)
sequenceDiagram
participant S as Scheduler
participant DB as DB
participant P4 as phase4
participant RA as Remote AP (C3)
S->>DB: Query outbound_transaction where status = failed and next_retry_dt <= now (batched, SKIP LOCKED)
DB-->>S: list of pending
loop For each failed transaction
S->>P4: Trigger AS4 send
P4->>RA: AS4 UserMessage
RA-->>P4: AS4 Receipt/Error
P4-->>S: AS4 result
S->>DB: Insert sending attempt row
S->>DB: Update outbound_transaction status
opt On success
S->>S: Trigger reporting callback
end
opt Max retries exhausted
S->>S: Trigger notification
end
end
MLS Reception Flow (asynchronous, initiated by remote C3)
sequenceDiagram
participant RA as Remote AP (C3)
participant PS as phase4 Servlet
participant DB as DB
RA->>PS: AS4 UserMessage (MLS response)
PS->>PS: Parse MLS, extract SBDH Instance ID reference
PS->>DB: Lookup outbound_transaction by SBDH Instance ID
DB-->>PS: original transaction
PS->>DB: Update outbound_transaction MLS fields (mls_status, mls_received_dt, mls_id)
PS-->>RA: AS4 Receipt
Outbound Message Metadata
outbound_transaction (one row per document)
| Field | Description |
|---|---|
| transaction_type | Type of outbound transaction: business_document or mls_response |
| sender_id | Peppol Participant ID of the sender |
| receiver_id | Peppol Participant ID of the receiver |
| doc_type_id | Peppol Document Type Identifier |
| process_id | Peppol Process Identifier |
| sbdh_instance_id | Peppol SBDH Instance Identifier |
| source_type | How the document was submitted: payload_only (AP creates SBDH) or prebuilt_sbd (SBDH already present) |
| document_path | Path to the document file — either an absolute filesystem path or an s3://bucket/key URI depending on storage.mode |
| document_size | Size of the document in bytes |
| document_hash | SHA-256 hash of the document payload for integrity verification |
| c1_country_code | Country of the sender (C1) |
| sbdh_standard | SBDH Standard override for non-XML payloads (e.g. urn:peppol:doctype:pdf+xml). Null if auto-derived. Set via sbdhStandard query parameter on the submit endpoint. |
| sbdh_type_version | SBDH TypeVersion override (e.g. 0). Null if auto-derived. Set via sbdhTypeVersion query parameter on the submit endpoint. |
| sbdh_type | SBDH Type override (e.g. factur-x). Null if auto-derived. Set via sbdhType query parameter on the submit endpoint. |
| payload_mime_type | MIME type for binary payloads (e.g. application/pdf). Null for XML payloads. Set via payloadMimeType query parameter on the submit endpoint. When set, the payload is wrapped in <BinaryContent> instead of being embedded as XML. |
| status | pending, rejected, sending, sent, failed, permanently_failed. Note: rejected is defined but currently never set for outbound transactions — a verification failure prevents the transaction from being created in the first place. |
| attempt_count | Total number of sending attempts (default 0) |
| created_dt | When the transaction was initially created |
| completed_dt | When the transaction reached its final successful state |
| reporting_status | Whether outbound reporting has been triggered (pending, reported, excluded) |
| next_retry_dt | Planned date/time of the next sending retry (null unless status is failed) |
| error_details | Summary error from the permanently failed attempt |
| mls_to | MLS_TO override (only for business_document) |
| mls_status | MLS reception status: pending, received_ap, received_ab, received_re, not_applicable (only for business_document) |
| mls_received_dt | When the MLS response was received (only for business_document) |
| mls_id | The MLS message ID from the received MLS (only for business_document) |
| mls_inbound_transaction_id | FK to the inbound_transaction that triggered this MLS response (only for mls_response) |
| custom1 | Optional free-text custom field (max 255 chars). Null if not set. Set via custom1 query parameter (or custom1 in the /submit-s3 JSON body) on the submit endpoints. Since 0.11.0. |
| custom2 | Optional free-text custom field (max 255 chars). Null if not set. Set via custom2 query parameter (or custom2 in the /submit-s3 JSON body) on the submit endpoints. Since 0.11.0. |
| custom3 | Optional free-text custom field (max 255 chars). Null if not set. Set via custom3 query parameter (or custom3 in the /submit-s3 JSON body) on the submit endpoints. Since 0.11.0. |
outbound_sending_attempt (one row per sending try)
| Field | Description |
|---|---|
| outbound_transaction_id | FK to the parent outbound transaction |
| as4_message_id | AS4 Message ID used for this attempt |
| as4_timestamp | AS4 MessageInfo/Timestamp from this attempt (UTC). MLS Milestone M1 for business documents; M2 for MLS responses. |
| receipt_message_id | AS4 Message ID from the synchronous receipt |
| http_status_code | HTTP status code from the AS4 response |
| attempt_dt | Date and time of this sending attempt |
| attempt_status | Outcome of this attempt (success, failed) |
| error_details | Error message or reason for failure |