Skip to main content
Before pn-paper-channel can route a letter to a postal operator, it must determine the correct delivery address. PaperAddressServiceImpl.getCorrectAddress() is the central entry point. It implements a priority-ordered lookup that adapts based on whether the request is a first attempt, a second attempt with a discovered address, or a second attempt requiring a National Registry lookup.

Priority order

1

Receiver address (first attempt)

The address provided by the caller in PrepareRequest.receiverAddress is the starting point for every request. It is saved in DynamoDB as a PnAddress entity with addressType = RECEIVER_ADDRESS and referenced by requestId.For a first attempt (no relatedRequestId), this address is used directly after normalization by Address Manager.
2

Discovered address (second attempt — postman flow)

When the first delivery fails and the postman has found an alternative address, the caller includes PrepareRequest.discoveredAddress in the second attempt’s request. pn-paper-channel saves this as addressType = DISCOVERED_ADDRESS.PaperAddressServiceImpl.getAddressFromDiscoveredAddress() retrieves the stored discovered address and passes it to SecondAttemptFlowHandlerFactory with flow type POSTMAN_FLOW.
3

National Registry lookup (second attempt — no discovered address)

When the second attempt has no discovered address (i.e. PrepareRequest.discoveredAddress is null), pn-paper-channel falls back to querying the National Registry:
  1. NationalRegistryService.finderAddressFromNationalRegistries() is called with the recipient’s fiscalCode, receiverType, and iun.
  2. The PnDeliveryRequest is set to status NATIONAL_REGISTRY_WAITING (PC002) and stored with a correlationId.
  3. When the registry responds asynchronously, the correlationId is used to resume the Prepare phase via requestDeliveryDAO.getByCorrelationId().
  4. If the registry returns no address, PnUntracebleException is raised with FailureDetailCodeEnum.D00 and the request moves to UNTRACEABLE (PC010).

Address Manager integration

After the correct address is chosen, pn-paper-channel submits it to Address Manager for normalization. Address Manager validates and standardizes postal codes, city names, and country codes. If Address Manager returns an error, the flow behaviour is controlled by two configuration flags:
FlagPropertyDefaultBehaviour when true
PNADDR001pn.paper-channel.pnaddr001continue-flowtrueContinue with the unnormalized address when Address Manager fails on a first-attempt request
PNADDR002pn.paper-channel.pnaddr002continue-flowfalseContinue with the unnormalized address when Address Manager fails on a second-attempt request
PNADDR002 defaults to false, meaning that if Address Manager fails during a second delivery attempt, the flow stops and the error is recorded. Set this to true only if your environment requires fallback behaviour for second attempts.
Address Manager errors are retried up to pn.paper-channel.attemptQueueAddressManager times via ToggleableQueueListener.handleAddressManagerErrorEventFromPreparePhaseOne() before a PnRequestError is recorded and the flow is abandoned.

Address entity: PnAddress

PnAddress is stored in the AddressDynamoTable DynamoDB table. The partition key is requestId and the sort key is addressType (called typology in the Java entity). All personally-identifiable fields are encrypted at rest using AWS KMS (see Encryption below).
DynamoDB attributeJava fieldEncryptedDescription
requestIdrequestIdNoParent delivery request identifier (PK)
addressTypetypologyNoAddress role: RECEIVER_ADDRESS, DISCOVERED_ADDRESS, SENDER_ADDRES, AR_ADDRESS, or EXECUTION (SK)
fullNamefullNameYesRecipient full name
nameRow2nameRow2YesSecond name line (e.g. company name)
addressaddressYesStreet address
addressRow2addressRow2YesSecond address line
capcapYesPostal code
citycityYesCity
city2city2YesSecondary city (district)
prprYesProvince code
countrycountryYesCountry (blank for domestic Italy)
ttlttlNoDynamoDB TTL timestamp (epoch seconds)

Address encryption with KMS

All @ToString.Exclude-annotated fields in PnAddress are encrypted before storage using AWS KMS. The implementation class is KmsEncryptionImpl, wired with the @Qualifier("kmsEncryption") qualifier.

How encryption works

  1. AddressDAOImpl is constructed with a DataEncryption kmsEncryption dependency.
  2. BaseDAO (the DynamoDB base class) intercepts write and read operations and applies kmsEncryption.encode() / kmsEncryption.decode() on annotated fields.
  3. KmsEncryptionImpl calls KmsClient.encrypt() with the configured KMS key and RSAES_OAEP_SHA_256 algorithm, then Base64-encodes the result.
  4. On read, KmsEncryptionImpl calls KmsClient.decrypt() using the context and algorithm stored in the encoded string via EncryptedUtils.
The KMS key ID and algorithm are read from aws.kms.* properties (AwsKmsProperties).
Address fields are also hashed for change detection. Address.convertToHash() produces a deterministic SHA-256 hash stored in PnDeliveryRequest.addressHash. This lets pn-paper-channel detect when the resolved address has changed between a first and second attempt without decrypting the stored value.

CheckAddress flow

The GET /paper-channel-private/v1/{requestId}/check-address endpoint allows callers to verify whether a delivery address is still valid. It returns a CheckAddressResponse containing the requestId and the endValidity timestamp. This is used by pn-delivery-push to confirm that a previously resolved address can still be used before submitting the Send request.

Second attempt flow detail

When relatedRequestId is set on a PrepareRequest, PaperMessagesServiceImpl.preparePaperSync() follows the second-attempt path:
  1. Loads the original PnDeliveryRequest using relatedRequestId to verify it exists.
  2. Loads the original receiver address from AddressDAO.
  3. Saves a new PnDeliveryRequest with the new requestId and sets relatedRequestId to point to the first attempt.
  4. Decides which sub-flow to run:
    • Discovered address present: calls prepareFlowStarter.startPreparePhaseOneFromPrepareSync() to normalize and validate the discovered address.
    • No discovered address: calls nationalRegistryService.finderAddressFromNationalRegistries() and parks the request at NATIONAL_REGISTRY_WAITING.
  5. Returns a PnPaperEventException to signal to the synchronous caller that processing is asynchronous (the caller should poll GET /paper-deliveries-prepare/{requestId}).
PaperAddressServiceImpl.chooseAddress() implements the branching logic:
// correlationId set → National Registry callback flow
if (StringUtils.isNotBlank(deliveryRequest.getCorrelationId())) {
    return getAddressFromNationalRegistry(deliveryRequest, fromNationalRegistry, addressFromFirstAttempt);
}
// relatedRequestId set → discovered address or second postman attempt
if (StringUtils.isNotBlank(deliveryRequest.getRelatedRequestId())) {
    return getAddressFromDiscoveredAddress(deliveryRequest, addressFromFirstAttempt);
}
// First attempt — use receiver address directly
return Mono.just(addressFromFirstAttempt);
Use GET /paper-channel-private/v1/b2b/paper-deliveries-prepare/{requestId} to poll the outcome of a prepare request. A PrepareEvent with statusCode = OK contains the confirmed address. A PrepareEvent with statusCode = KO means the recipient is untraceable.

Build docs developers (and LLMs) love