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
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.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.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:NationalRegistryService.finderAddressFromNationalRegistries()is called with the recipient’sfiscalCode,receiverType, andiun.- The
PnDeliveryRequestis set to statusNATIONAL_REGISTRY_WAITING(PC002) and stored with acorrelationId. - When the registry responds asynchronously, the
correlationIdis used to resume the Prepare phase viarequestDeliveryDAO.getByCorrelationId(). - If the registry returns no address,
PnUntracebleExceptionis raised withFailureDetailCodeEnum.D00and the request moves toUNTRACEABLE(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:| Flag | Property | Default | Behaviour when true |
|---|---|---|---|
PNADDR001 | pn.paper-channel.pnaddr001continue-flow | true | Continue with the unnormalized address when Address Manager fails on a first-attempt request |
PNADDR002 | pn.paper-channel.pnaddr002continue-flow | false | Continue with the unnormalized address when Address Manager fails on a second-attempt request |
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 attribute | Java field | Encrypted | Description |
|---|---|---|---|
requestId | requestId | No | Parent delivery request identifier (PK) |
addressType | typology | No | Address role: RECEIVER_ADDRESS, DISCOVERED_ADDRESS, SENDER_ADDRES, AR_ADDRESS, or EXECUTION (SK) |
fullName | fullName | Yes | Recipient full name |
nameRow2 | nameRow2 | Yes | Second name line (e.g. company name) |
address | address | Yes | Street address |
addressRow2 | addressRow2 | Yes | Second address line |
cap | cap | Yes | Postal code |
city | city | Yes | City |
city2 | city2 | Yes | Secondary city (district) |
pr | pr | Yes | Province code |
country | country | Yes | Country (blank for domestic Italy) |
ttl | ttl | No | DynamoDB 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
AddressDAOImplis constructed with aDataEncryption kmsEncryptiondependency.BaseDAO(the DynamoDB base class) intercepts write and read operations and applieskmsEncryption.encode()/kmsEncryption.decode()on annotated fields.KmsEncryptionImplcallsKmsClient.encrypt()with the configured KMS key andRSAES_OAEP_SHA_256algorithm, then Base64-encodes the result.- On read,
KmsEncryptionImplcallsKmsClient.decrypt()using the context and algorithm stored in the encoded string viaEncryptedUtils.
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
TheGET /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
WhenrelatedRequestId is set on a PrepareRequest, PaperMessagesServiceImpl.preparePaperSync() follows the second-attempt path:
-
Loads the original
PnDeliveryRequestusingrelatedRequestIdto verify it exists. -
Loads the original receiver address from
AddressDAO. -
Saves a new
PnDeliveryRequestwith the newrequestIdand setsrelatedRequestIdto point to the first attempt. -
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 atNATIONAL_REGISTRY_WAITING.
- Discovered address present: calls
-
Returns a
PnPaperEventExceptionto signal to the synchronous caller that processing is asynchronous (the caller should pollGET /paper-deliveries-prepare/{requestId}).
PaperAddressServiceImpl.chooseAddress() implements the branching logic: