Overview
A signer receives an email containing a signing link. Signer clicks the link, which routes through a Salesforce Site to PDFESigningScreen.page. The page decodes the link parameter and loads documentSigningScreen LWC. The LWC orchestrates the signing workflow: OTP verification (if enabled), form field population, document display in iframe (via PDFSigningScreen.page or AutoPopulatedSigningScreen.page or AutoPopulatedSigningScreenWord.page), field validation, signature capture, and final PKCS7 digital signing. On success, the LWC displays status confirmation and optionally downloads the signing certificate. The signing flow is the final user-facing layer that bridges OTP → Document Viewing → Signing → Workflow Completion.
Five Visualforce pages orchestrate signing:
|
Page |
Purpose |
Context |
|---|---|---|
|
|
Entry point (Salesforce Site). Parses URL params, loads LWC. |
Public-facing (unauthenticated) |
|
|
Orchestrator. Handles OTP, status, iframe routing. |
Lightning component (embedded) |
|
|
PDF form signing. Nutrient Web SDK + field validation + PKCS7. |
Iframe inside LWC |
|
|
Alternative PDF signing. Auto-populated form fields. |
Iframe inside LWC (variant) |
|
|
Alternative PDF signing. Auto-populated form fields on Word Documents. |
Iframe inside LWC (variant) |
Architecture.
┌──────────────────────────────────────────────────┐
│ Signer clicks email link │
│ (Document_Workflow_Item__c.Signer_URL_New__c) │
└────────────────────┬─────────────────────────────┘
│
Via Salesforce Site
│
▼
┌────────────────────────┐
│ PDFESigningScreen.page │
│ (public VF wrapper) │
│ │
│ Parse URL params: │
│ ?sign=encoded_link │
└────────────┬───────────┘
│
Load documentSigningScreen LWC
(into #LightningComponent div)
│
┌────────────▼───────────────┐
│ documentSigningScreen LWC │
│ (orchestrator) │
│ │
│ • Decode urlParam │
│ • Check OTP requirements │
│ • Query workflow state │
│ • Verify signer status │
└────────────┬───────────────┘
│
┌────────────▼───────────────┐
│ OTP Verification? │
└────────┬──────────┬────────┘
│ (yes) │ (no)
▼ ▼
c-document- Load Signing
signing-o-t-p- Page Iframe
screen │
│ │
User enters OTP ▼
│ ┌─────────────────────┐
│ │ PDFSigningScreen.page│
│ │ OR │
│ │ AutoPopulated...page │
│ │ │
│ │ Nutrient Web SDK │
│ │ renders PDF │
│ │ + form fields │
│ │ │
│ │ User fills/signs │
│ │ invokeSignatureService│
│ │ → PKCS7 hash │
│ │ → ContentVersion │
│ │ → Status update │
│ └─────────┬────────────┘
│ │
└───────┬───────┘
│
documentSigningScreen receives
signing result from iframe
│
┌────────────▼───────────────┐
│ Show Status Screen │
│ • Success / Declined │
│ • Error / Verification │
│ • Certificate download │
│ • Redirect (if configured)│
└────────────────────────────┘
Key mechanism — link-based signing orchestration + multi-path VF pages
Signing starts via a public link (unauthenticated Salesforce Site). PDFESigningScreen.page acts as a router: it parses the URL-encoded signing link parameter, then loads documentSigningScreen LWC into a Lightning container. The LWC decodes the link, queries the Document_Workflow__c and Document_Workflow_Item__c to verify signer status, check OTP requirements, and determine the signing method (PDF vs Word, auto-populated vs manual). Based on configuration, the LWC either:
-
Shows OTP verification screen (c-document-signing-o-t-p-screen child component)
-
Directly loads a VF page (PDFSigningScreen or AutoPopulatedSigningScreen) in an iframe
After OTP (if required), the iframe loads PDFSigningScreen.page (or variant), which calls its Apex controller to fetch field config and PDF blob. The user fills form fields and signs. The VF page calls invokeSignatureService(), which PKCS7-signs server-side, then posts the result back to the LWC via postMessage. The LWC receives the signing result and displays the final status screen (success, declined, error, etc.). This architecture allows guest link sharing (unauthenticated) + OTP gating + flexible signing methods (PDF/Word, auto-populated/manual) + certificate management.
VF Page — PDFESigningScreen.page
public with sharing (Salesforce Site accessible). Entry point for all signing links. No Apex controller. Uses Lightning Out to load LWC components.
|
Feature |
Implementation |
|---|---|
|
Container |
|
|
Lightning App |
|
|
Components |
Loads either |
|
URL Routing |
Checks |
|
Error Handling |
Shows red alert div if no query params or invalid params |
|
Favicon |
Sets Cloud Maven favicon from static resource |
Query Parameter Routing:
|
Param |
Component |
Purpose |
|---|---|---|
|
|
documentSigningScreen |
Routes to signing workflow |
|
|
generateAndCreateRecord |
Routes to document generation workflow |
|
(none) |
alert div |
Shows error message |
LWC — documentSigningScreen
Orchestrator component. Receives @api urlParam (URL-encoded signing link). Handles OTP verification, document status checking, iframe routing, and final status display.
Tracked state
|
Property |
Purpose |
|---|---|
|
|
Injected by PDFESigningScreen. URL-encoded Document_Workflow_Item__c signing link. |
|
|
Spinner during initialization and link decoding. |
|
|
Toggles OTP verification screen (child component c-document-signing-o-t-p-screen). |
|
|
Toggles between OTP screen and document-signing screen. |
|
|
URL to PDFSigningScreen.page or AutoPopulatedSigningScreen.page (loaded in iframe). |
|
|
If Document_Workflow_Item__c status = Document Signed. |
|
|
If Document_Workflow__c expiry date passed. |
|
|
If signer declines document. |
|
|
If OTP verification fails (max attempts exceeded). |
|
|
Array of OTP delivery methods (SMS, Email, etc.). |
|
|
Countdown timer for OTP resend. |
|
|
Status screen config (type: success/declined/error/verified, message, icon). |
|
|
Document_Workflow__c ID (parsed from urlParam). |
|
|
Signer name (from Document_Workflow_Item__c). |
|
|
Boolean. If true, shows "completion email" message after signing. |
|
|
If configured, auto-downloads signing certificate on success. |
|
|
If signer consent modal required + accepted. |
Key functions
|
Function |
Behavior |
|---|---|
|
|
Decodes urlParam via UtilityClass.encodeDecodeURLString(). Queries workflow + signer. Calls checkifUserAlreadySigned(), getWorkflowStatus(). Populates OTP methods if required. |
|
|
Decodes URL parameter. Validates signing link exists and is not expired. Sets dWorkFlowId + currentSigner. |
|
|
Queries Document_Workflow_Item__c by encoded link. Checks status (if already signed, declined, etc.). Checks workflow expiry. Sets appropriate status/flags. |
|
|
Fired when OTP verification succeeds (from child c-document-signing-o-t-p-screen). Sets showOTPScreen = false, documentGenerated = true. Calls loadSigningFrame(). |
|
|
Fired when OTP fails (max attempts). Sets bOtpVerificationFailed = true, shows error status screen. |
|
|
Determines signing page type: if PDF → PDFSigningScreen.page, if Word → AutoPopulatedSigningScreenWord.page. If auto-populate enabled → AutoPopulatedSigningScreen.page. Constructs URL with workflowId. Sets generatedDocumentURL (triggers iframe load). |
|
|
Listens for postMessage from iframe (PDFSigningScreen.page). Receives { Success, signedHash, errorMessage, signatureTimeStamps }. If success: calls saveDocument() (if needed), then displays success status screen. If error: shows error status screen. |
|
|
Apex call to SigningScreenController.saveDocument(). Saves signed PDF. Called if document not auto-saved in iframe. |
|
|
Calls SigningScreenController.declineDocument() on workflow + signer. Updates status to declined, shows declined status screen. |
|
|
Calls SigningScreenController.downloadSignerCertificate(). Auto-downloads certificate if configured. |
|
|
Checks bAutoDownloadCertificate flag. If true and signing succeeded: auto-triggers certificate download. |
|
|
If consent modal required: calls SigningScreenController.recordSignerConsent() to log acceptance. |
|
|
Displays custom message/status on signing screen. |
Child Components
|
Component |
Usage |
|---|---|
|
|
OTP verification. Takes otpMethods, urlParam, otpResendTime, otpAttempts. Fires userVerified/verificationFailed events. |
|
|
Status display. Takes statusConfig (type, message, icon, buttons). Shows success/error/declined/expired. |
VF Page — PDFSigningScreen.page
Controller: SigningScreenController. Hosts Nutrient Web SDK iframe for PDF signing. Loaded inside documentSigningScreen LWC iframe.
|
Method |
Returns |
What it does |
|---|---|---|
|
|
String |
VF page getter. Reads page params: signerURLId, workflowId (alternative: templateId, recordId). Queries workflow item + workflow + template. Calls DocumentGenerationService.getFormData(). Sets VF properties: base64String (PDF), dataMap, readOnlyFields, requiredFields, dateFields, picklistMap, fieldNameLabelMap, licensekey. Returns 'Success' or 'Error'. |
|
|
String |
Receives PDF blob (digest) + workflowId. Calls SigningService.signDocument(). Generates PKCS7, creates ContentVersion, updates workflow status, creates audit events. Returns { Success, signedHash, signatureTimeStamps }. Posts back to LWC via window.parent.postMessage(). |
Key VF Properties:
|
Property |
Purpose |
|---|---|
|
|
Base64-encoded PDF blob. Passed to NutrientViewer.load(). |
|
|
JSON field→value map for pre-population. |
|
|
Fields that cannot be edited. Set widget.readOnly = true. |
|
|
Fields required before signing. Validated before sign click. |
|
|
Date fields for formatting (MM/DD/YYYY). |
|
|
Field name → options array for dropdown population. |
|
|
Nutrient Web SDK license key from org config. |
|
|
If true, shows error overlay (required fields null). |
|
|
PKCS7 signature hash returned by invokeSignatureService(). |
VF Page — AutoPopulatedSigningScreen.page
Controller: AutoPopulatedSigningScreenController. Alternative signing page where form fields are auto-populated from template mappings and workflow configuration. User signs without manually filling fields.
|
Method |
Returns |
What it does |
|---|---|---|
|
|
String |
VF page getter. Reads workflowId from page params. Queries Document_Workflow__c, Document_Template__c. Fetches field config from DocumentGenerationService.getFormData(). Auto-populates dataMap with workflow values. Sets VF properties (same as PDFSigningScreen). Returns 'Success' or 'Error'. |
|
|
String |
Same as PDFSigningScreen. Receives PDF + workflowId, PKCS7-signs, saves, updates status. |
VF Page — AutoPopulatedSigningScreenWord.page
Controller: AutoPopulatedSigningScreenController (same). Used for Word document (.docx) signing instead of PDF. Processes Word field merges before signing.
Apex — SigningScreenController
public with sharing. Backs PDFSigningScreen and AutoPopulatedSigningScreen VF pages. Handles signing workflow invocation.
|
Method |
Returns |
What it does |
|---|---|---|
|
|
String |
Encodes/decodes signing link for URL safety. Called by documentSigningScreen LWC to decode urlParam. |
|
|
Boolean |
Checks if Document_Workflow_Item__c already signed (Event_Type = Document Signed). |
|
|
Map |
Queries workflow status (Awaiting Signature, Signature Completed, Workflow Completed, Failed, Expired). |
|
|
Integer |
Returns OTP attempt count for rate-limiting. |
|
|
void |
Updates workflow + signer item status to declined. Creates audit event. |
|
|
Blob |
Returns signer certificate (.pfx or .cer) for download. |
|
|
void |
Auto-downloads certificate (browser-side JavaScript trigger). |
|
|
void |
Logs signer consent acceptance for compliance. |
|
|
void |
Logs that signer clicked link (audit trail). |
|
|
Map |
Returns custom messages (decline reason, success text, etc.). |
Apex — DocumentSigningUtility
Helper class for signing operations.
|
Method |
Purpose |
|---|---|
|
|
Constructs and sends email (used for signing confirmations). |
Apex — WS_EoriginalService / WS_DigitalSigning
Web service integration classes. Handle e-signature service callouts (DocuSign, Adobe Sign, or eOriginal vaulting).
|
Class |
Purpose |
|---|---|
|
|
Callout to eOriginal service for document vaulting/notarization. |
|
|
Callout to digital signature provider for PKCS7/eIDAS signing. |
|
|
Queued async job for e-Original vaulting (doesn't block signing). |
Data Flow — Signing Workflow Start to Completion
-
Signer receives email with Document_Workflow_Item__c.Signer_URL_New__c link.
-
Signer clicks link. Redirects via Salesforce Site (public).
-
Salesforce Site routes to PDFESigningScreen.page with
?sign=URLEncodedLinkparam. -
PDFESigningScreen.page loads documentSigningScreen LWC via Lightning Out.
-
documentSigningScreen LWC connectedCallback() executes.
-
Calls UtilityClass.encodeDecodeURLString(urlParam, 'decode') → decodes link.
-
Queries Document_Workflow_Item__c by decoded link.
-
Queries linked Document_Workflow__c.
-
Calls SigningScreenController.checkifUserAlreadySigned() → if already signed, sets alreadySigned=true, shows status screen. Return.
-
Calls SigningScreenController.getWorkflowStatus() → checks expiry, status, etc. If expired, shows expiry screen. Return.
-
Calls SigningScreenController.getOtpFailCount() → retrieves OTP attempt count.
-
Queries Document_Template__c to check OTP requirement (Enable_OTP_On_Signing__c, OTP_Methods__c).
-
If OTP required: calls SigningScreenController.getTemplateConfigs() → retrieves OTP methods. Populates otpMethods array. Sets showOTPScreen=true.
-
Renders c-document-signing-o-t-p-screen child component.
-
Signer enters OTP. c-document-signing-o-t-p-screen calls UtilityClass.verifyOTP() (server-side). On success: fires userVerified event. On fail: fires verificationFailed event + increments OTP attempt counter.
-
documentSigningScreen LWC receives userVerified event.
-
Sets showOTPScreen=false, documentGenerated=true.
-
Calls loadSigningFrame().
-
Determines signing page: if PDF → PDFSigningScreen.page. If Word → AutoPopulatedSigningScreenWord.page. If auto-populate → AutoPopulatedSigningScreen.page. (Check Document_Template__c.Is_Document_PDF__c and Disable_Form_Field_Editing__c).
-
Constructs URL:
/apex/PDFSigningScreen?workflowId={workflowId}(or similar). -
Sets generatedDocumentURL. HTML renders
<iframe src="{generatedDocumentURL}" />. -
Iframe loads PDFSigningScreen.page.
-
PDFSigningScreen.page controller calls getTemplateData() getter.
-
Reads page param workflowId. Queries workflow + signer + template.
-
Calls DocumentGenerationService.getFormData() → returns dataMap, readOnlyFields, requiredFields, dateFields, picklistMap, base64String (PDF).
-
Sets VF properties.
-
VF page renders. JavaScript calls loadConfigurations() → builds Maps.
-
JavaScript calls loadPDF() → NutrientViewer.load(base64String, licensekey).
-
Nutrient renders PDF + form fields in container.
-
JavaScript calls configureFormFields() → sets read-only on readOnlyFields, applies date formatting, populates picklists.
-
Signer views PDF + form fields.
-
Signer fills fields + adds signature via Nutrient signature widget.
-
Signer clicks "Sign" button.
-
JavaScript calls signDocument().
-
validateMissingFields() → checks all requiredFields filled. If any null: applyFieldBorder() highlights red, blocks sign.
-
If all filled: signatureProcess().
-
Validates at least one signature field has ink/image/text.
-
Calls instance.exportPDF() → captures PDF with form values + user signatures.
-
Encodes PDF as base64.
-
Calls generatePKCS7() → prepares PKCS7 structure.
-
POSTs to callSigningMethod (apex:actionFunction) with base64 digest + workflowId.
-
SigningScreenController.invokeSignatureService(digest, workflowId) executes server-side.
-
Calls SigningService.signDocument(digest, workflowId).
-
SigningService generates PKCS7 hash using org certificate.
-
Creates ContentVersion with signed PDF.
-
Updates Document_Workflow__c status → Signature Completed.
-
Creates Document_Workflow_Item__c event (Document Signed) with Digitally_Signed_Timestamp__c.
-
Checks if last signer. If yes: updates Document_Workflow__c status → Workflow Completed.
-
Returns { Success: true, signedHash (PKCS7 hex), signatureTimeStamps (JSON array) }.
-
JavaScript receives response via oncomplete callback of apex:actionFunction.
-
Calls window.parent.postMessage({ type: 'SigningResult', payload: { Success, signedHash, ... } }, vfOrigin).
-
documentSigningScreen LWC (in parent) receives postMessage.
-
Calls handleSigningResult(event).
-
If Success: calls saveDocument() (if needed). Calls SigningScreenController.recordSignerConsent() (if consent required).
-
Checks if bAutoDownloadCertificate. If yes: calls downloadSigningCertificate() → auto-downloads .pfx or .cer.
-
Sets statusConfig = { type: 'success', message: 'Document signed successfully!', icon: success_image, buttons: [Download, Close/Redirect] }.
-
Sets documentGenerated=true, showSpinner=false.
-
Renders c-status-screen child component (displays status).
-
Signer clicks Download Certificate (or auto-downloaded).
-
SigningScreenController.downloadSigningCertificate() returns blob.
-
Browser downloads certificate file.
-
Signer clicks Close or page auto-redirects (if bRedirectionRequired and redirectionUrl set).
-
Sends confirmation email to signer + next signer (if exists) or document owner.