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

  1. 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.
  2. Store document - The document is written to disk under storage.outbound.path; size and SHA-256 hash are computed while streaming.
  3. 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, the onOutboundVerificationRejection notification is triggered, an error is returned to the caller, and no sending attempt is made.
  4. Persist transaction - The file path and metadata are persisted to the outbound_transaction table in the database in pending state.
  5. 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_failed without 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.
  6. Record sending attempt - Insert a row into outbound_sending_attempt with the outcome (AS4 Message ID, Receipt Message ID, HTTP status code, or error details). Update the parent outbound_transaction status accordingly.
  7. On success: trigger outbound reporting - After a successful send, a Peppol Reporting item is created (via phase4's createPeppolReportingItemAfterSending) and stored, and the onOutboundDocumentSent lifecycle event is fired. The API response only signals overall success when the reporting item was stored as well.
  8. 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.
  9. On permanent failure: notify - When max retries are exhausted, the generic notification interface is invoked.
  10. 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