- Ya
- 7 Agosti 2026, 06:51 UTC
- Mwandishi
- Kamo
- Ahadi ya
- d8bc8c4
Everything after a signer presses Finish was incomplete. Notices: an envelope's last signature produced a flattened PDF in MinIO that only the final signer ever saw a link to, for the four hours their session lived. Earlier signers and the sender learned nothing, while the signer app told every one of them a signed copy would be emailed. Adds EsignNotificationService: each party's executed copy (re-minting their access token and extending it to a stated 90-day window), the sender's completion notice, and the decoder / void / expiry notices. Every send is deferred to afterCommit — mail cannot be recalled if the signing transaction rolls back. Routing order: SigningSession reported SEQUENTIAL whenever the roster carried distinct routing orders, and nothing enforced it — every recipient was emailed at once and any of them could sign at any time. EsignRouting now decides the open tier; the initial send invites only that tier, a signature that moves it invites the next, and an out-of-turn submit is a 409. Guards: a DELEGATED recipient could still sign the slot they had just reassigned; decoder() had no terminal-state check at all, so a party who had already signed could flip themselves to DEcoderD on an executed envelope; delegate() could add a live recipient to a closed one. A decoder now closes the envelope rather than leaving the remaining parties signing something envelopeComplete can never call complete. Expiry: EsignEnvelopeStatus.EXPIRED existed, had a UI chip, and was never written by any code path. Adds EsignExpirySweeper (+ @EnableScheduling). Also: the step-up OTP was six digits with unlimited attempts against a four-hour session — now attempt-capped and compared in constant time; a failed resend answered 200 ("Invite resent" for mail that never left); the sender's covering message was never persisted, so a resend and every tier-advance invitation dropped it and session.message was always null; **************** pointed inside the signer app's own BFF where no sender-side consumer could call it; a template with no fields at all could be sent and "signed"; and ResponseStatusException reasons never reached the client, so every carefully worded refusal arrived as a bare status code. 103 -> 140 tests, 0 failures.