Skip to content

Commit 34d0cf6

Browse files
Merge pull request #67 from docusign/feature/responsive-signing
Added responsive signing example
2 parents 9de8e42 + 1121510 commit 34d0cf6

File tree

6 files changed

+349
-1
lines changed

6 files changed

+349
-1
lines changed

index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ const {
2727
eg009, eg010, eg011, eg012, eg013, eg014, eg015,
2828
eg016, eg017, eg018, eg019, eg020, eg022, eg023,
2929
eg024, eg025, eg026, eg027, eg028, eg029, eg030,
30-
eg031, eg032, eg033, eg034, eg035, eg036, eg037
30+
eg031, eg032, eg033, eg034, eg035, eg036, eg037,
31+
eg038
3132
} = require("./lib/eSignature/controllers");
3233

3334
const {
@@ -223,6 +224,8 @@ if (examplesApi.examplesApi.isRoomsApi) {
223224
.post('/eg036', eg036.createController)
224225
.get('/eg037', eg037.getController)
225226
.post('/eg037', eg037.createController)
227+
.get('/eg038', eg038.getController)
228+
.post('/eg038', eg038.createController)
226229
}
227230

228231
function dsLoginCB1(req, res, next) { req.dsAuthCodeGrant.oauth_callback1(req, res, next) }
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @file
3+
* Example 038: Signable HTML document
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path');
8+
const validator = require('validator');
9+
const dsConfig = require('../../../config/index.js').config;
10+
const { sendEnvelope } = require('../examples/responsiveSigning');
11+
12+
const eg038ResponsiveSigning = exports;
13+
const eg = 'eg038'; // This example reference.
14+
const mustAuthenticate = '/ds/mustAuthenticate';
15+
const minimumBufferMin = 3;
16+
const demoDocsPath = path.resolve(__dirname, '../../../demo_documents');
17+
const docFile = 'order_form.html';
18+
const dsReturnUrl = dsConfig.appUrl + '/ds-return';
19+
const dsPingUrl = dsConfig.appUrl + '/'; // Url that will be pinged by the DocuSign signing via Ajax
20+
21+
/**
22+
* Create the envelope
23+
* @param {object} req Request obj
24+
* @param {object} res Response obj
25+
*/
26+
eg038ResponsiveSigning.createController = async (req, res) => {
27+
// Step 1. Check the token
28+
// At this point we should have a good token. But we
29+
// double-check here to enable a better UX to the user.
30+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
31+
if (! isTokenOK) {
32+
req.flash('info', 'Sorry, you need to re-authenticate.');
33+
// Save the current operation so it will be resumed after authentication
34+
req.dsAuth.setEg(req, eg);
35+
res.redirect(mustAuthenticate);
36+
}
37+
38+
// Step 2. Call the worker method
39+
const { body } = req;
40+
const envelopeArgs = {
41+
signerEmail: validator.escape(body.signerEmail),
42+
signerName: validator.escape(body.signerName),
43+
ccEmail: validator.escape(body.ccEmail),
44+
ccName: validator.escape(body.ccName),
45+
status: 'sent',
46+
signerClientId: 1000,
47+
docFile: path.resolve(demoDocsPath, docFile),
48+
dsReturnUrl: dsReturnUrl,
49+
dsPingUrl: dsPingUrl
50+
};
51+
const args = {
52+
accessToken: req.user.accessToken,
53+
basePath: req.session.basePath,
54+
accountId: req.session.accountId,
55+
envelopeArgs: envelopeArgs
56+
};
57+
let results = null;
58+
59+
try {
60+
results = await sendEnvelope(args);
61+
}
62+
catch (error) {
63+
const errorBody = error && error.response && error.response.body;
64+
// we can pull the DocuSign error code and message from the response body
65+
const errorCode = errorBody && errorBody.errorCode;
66+
const errorMessage = errorBody && errorBody.message;
67+
// In production, may want to provide customized error messages and
68+
// remediation advice to the user.
69+
res.render('pages/error', {err: error, errorCode, errorMessage});
70+
}
71+
if (results) {
72+
res.redirect(results.redirectUrl);
73+
}
74+
}
75+
76+
/**
77+
* Form page for this application
78+
*/
79+
eg038ResponsiveSigning.getController = (req, res) => {
80+
// Check that the authentication token is ok with a long buffer time.
81+
// If needed, now is the best time to ask the user to authenticate
82+
// since they have not yet entered any information into the form.
83+
const isTokenOK = req.dsAuth.checkToken();
84+
if (isTokenOK) {
85+
sourceFile = (path.basename(__filename))[5].toLowerCase() + (path.basename(__filename)).substr(6);
86+
res.render('pages/examples/eg038ResponsiveSigning', {
87+
eg: eg, csrfToken: req.csrfToken(),
88+
title: "Signable HTML document",
89+
sourceFile: sourceFile,
90+
sourceUrl: dsConfig.githubExampleUrl + 'eSignature/examples/' + sourceFile,
91+
documentation: dsConfig.documentation + eg,
92+
showDoc: dsConfig.documentation
93+
});
94+
} else {
95+
// Save the current operation so it will be resumed after authentication
96+
req.dsAuth.setEg(req, eg);
97+
res.redirect(mustAuthenticate);
98+
}
99+
}

lib/eSignature/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ module.exports.eg034 = require('./eg034UseConditionalRecipients');
3434
module.exports.eg035 = require('./eg035ScheduledSending');
3535
module.exports.eg036 = require('./eg036DelayedRouting');
3636
module.exports.eg037 = require('./eg037SmsDelivery');
37+
module.exports.eg038 = require('./eg038ResponsiveSigning');
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* @file
3+
* Example 038: Signable HTML document
4+
* @author DocuSign
5+
*/
6+
7+
const fs = require("fs-extra");
8+
const docusign = require("docusign-esign");
9+
10+
/**
11+
* This function does the work of creating the envelope in
12+
* draft mode and returning a URL for the sender's view
13+
* @param {object} args object
14+
*/
15+
const sendEnvelope = async (args) => {
16+
// Data for this method
17+
// args.basePath
18+
// args.accessToken
19+
// args.accountId
20+
21+
let dsApiClient = new docusign.ApiClient();
22+
dsApiClient.setBasePath(args.basePath);
23+
dsApiClient.addDefaultHeader("Authorization", "Bearer " + args.accessToken);
24+
let envelopesApi = new docusign.EnvelopesApi(dsApiClient);
25+
26+
// Step 1. Make the envelope body
27+
let envelope = makeEnvelope(args.envelopeArgs);
28+
29+
// Step 2. Send the envelope
30+
let results = await envelopesApi.createEnvelope(args.accountId, {
31+
envelopeDefinition: envelope,
32+
});
33+
let envelopeId = results.envelopeId;
34+
35+
// Step 3. Create the recipient view
36+
let viewRequest = makeRecipientViewRequest(args.envelopeArgs);
37+
38+
// Call the CreateRecipientView API
39+
// Exceptions will be caught by the calling function
40+
results = await envelopesApi.createRecipientView(args.accountId, envelopeId, {
41+
recipientViewRequest: viewRequest,
42+
});
43+
44+
return { envelopeId: envelopeId, redirectUrl: results.url };
45+
};
46+
47+
/**
48+
* Creates envelope
49+
* @function
50+
* @param {Object} args parameters for the envelope
51+
* @returns {Envelope} An envelope definition
52+
* @private
53+
*/
54+
function makeEnvelope(args) {
55+
// Data for this method
56+
// args.signerEmail
57+
// args.signerName
58+
// args.ccEmail
59+
// args.ccName
60+
// args.status
61+
62+
// document (html) has tag /sn1/
63+
//
64+
// The envelope has two recipients.
65+
// recipient 1 - signer
66+
// recipient 2 - cc
67+
// The envelope will be sent first to the signer.
68+
// After it is signed, a copy is sent to the cc person.
69+
70+
// create a signer recipient to sign the document, identified by name and email
71+
// We're setting the parameters via the object constructor
72+
let signer = docusign.Signer.constructFromObject({
73+
email: args.signerEmail,
74+
name: args.signerName,
75+
clientUserId: args.signerClientId,
76+
recipientId: "1",
77+
routingOrder: "1",
78+
roleName: "Signer",
79+
});
80+
// routingOrder (lower means earlier) determines the order of deliveries
81+
// to the recipients. Parallel routing order is supported by using the
82+
// same integer as the order for two or more recipients.
83+
84+
// create a cc recipient to receive a copy of the documents, identified by name and email
85+
// We're setting the parameters via setters
86+
let cc = new docusign.CarbonCopy.constructFromObject({
87+
email: args.ccEmail,
88+
name: args.ccName,
89+
routingOrder: "2",
90+
recipientId: "2",
91+
});
92+
93+
// Add the recipients to the envelope object
94+
let recipients = docusign.Recipients.constructFromObject({
95+
signers: [signer],
96+
carbonCopies: [cc],
97+
});
98+
99+
// add the document
100+
101+
let htmlDefinition = new docusign.DocumentHtmlDefinition();
102+
htmlDefinition.source = getHTMLDocument(args);
103+
104+
let document = new docusign.Document();
105+
document.name = "doc1.html"; // can be different from actual file name
106+
document.documentId = "1"; // a label used to reference the doc
107+
document.htmlDefinition = htmlDefinition;
108+
109+
// create the envelope definition
110+
let env = new docusign.EnvelopeDefinition();
111+
env.emailSubject = "Example Signing Document";
112+
env.documents = [document];
113+
env.recipients = recipients;
114+
env.status = args.status;
115+
116+
return env;
117+
}
118+
119+
/**
120+
* Gets the HTML document
121+
* @function
122+
* @private
123+
* @param {Object} args parameters for the envelope
124+
* @returns {string} A document in HTML format
125+
*/
126+
127+
function getHTMLDocument(args) {
128+
// Data for this method
129+
// args.signerEmail
130+
// args.signerName
131+
// args.ccEmail
132+
// args.ccName
133+
134+
let docHTMLContent = fs.readFileSync(args.docFile, { encoding: "utf8" });
135+
136+
// Substitute values into the HTML
137+
// Substitute for: {signerName}, {signerEmail}, {ccName}, {ccEmail}
138+
return docHTMLContent
139+
.replace("{signerName}", args.signerName)
140+
.replace("{signerEmail}", args.signerEmail)
141+
.replace("{ccName}", args.ccName)
142+
.replace("{ccEmail}", args.ccEmail)
143+
.replace("/sn1/", "<ds-signature data-ds-role=\"Signer\"/>")
144+
.replace("/l1q/", "<input data-ds-type=\"number\"/>")
145+
.replace("/l2q/", "<input data-ds-type=\"number\"/>");
146+
}
147+
148+
function makeRecipientViewRequest(args) {
149+
// Data for this method
150+
// args.dsReturnUrl
151+
// args.signerEmail
152+
// args.signerName
153+
// args.signerClientId
154+
// args.dsPingUrl
155+
156+
let viewRequest = new docusign.RecipientViewRequest();
157+
158+
// Set the url where you want the recipient to go once they are done signing
159+
// should typically be a callback route somewhere in your app.
160+
// The query parameter is included as an example of how
161+
// to save/recover state information during the redirect to
162+
// the DocuSign signing. It's usually better to use
163+
// the session mechanism of your web framework. Query parameters
164+
// can be changed/spoofed very easily.
165+
viewRequest.returnUrl = args.dsReturnUrl + "?state=123";
166+
167+
// How has your app authenticated the user? In addition to your app's
168+
// authentication, you can include authenticate steps from DocuSign.
169+
// Eg, SMS authentication
170+
viewRequest.authenticationMethod = "none";
171+
172+
// Recipient information must match embedded recipient info
173+
// we used to create the envelope.
174+
viewRequest.email = args.signerEmail;
175+
viewRequest.userName = args.signerName;
176+
viewRequest.clientUserId = args.signerClientId;
177+
178+
// DocuSign recommends that you redirect to DocuSign for the
179+
// embedded signing. There are multiple ways to save state.
180+
// To maintain your application's session, use the pingUrl
181+
// parameter. It causes the DocuSign signing web page
182+
// (not the DocuSign server) to send pings via AJAX to your
183+
// app,
184+
viewRequest.pingFrequency = 600; // seconds
185+
// NOTE: The pings will only be sent if the pingUrl is an https address
186+
viewRequest.pingUrl = args.dsPingUrl; // optional setting
187+
188+
return viewRequest;
189+
}
190+
191+
module.exports = { sendEnvelope };
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<% include ../../partials/examplesHead %>
2+
3+
<h4>38. Signable HTML document</h4>
4+
<p>This example demonstrates how to create an HTML document for responsive signing.</p>
5+
6+
<% include ../../partials/docBody %>
7+
8+
<p>API method used:
9+
<a target ='_blank' href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a> and
10+
<a target ='_blank' href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/">EnvelopeViews::createRecipient</a>.
11+
</p>
12+
13+
<% include ../../partials/gitSource %>
14+
<form class="eg" action="" method="post" data-busy="form">
15+
<div class="form-group">
16+
<label for="signerEmail">Signer Email</label>
17+
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
18+
aria-describedby="emailHelp" placeholder="pat@example.com" required
19+
value="<%= locals.dsConfig.signerEmail %>">
20+
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
21+
</div>
22+
<div class="form-group">
23+
<label for="signerName">Signer Name</label>
24+
<input type="text" class="form-control" id="signerName" placeholder="Pat Johnson" name="signerName"
25+
value="<%= locals.dsConfig.signerName %>" required>
26+
</div>
27+
<div class="form-group">
28+
<label for="ccEmail">CC Email</label>
29+
<input type="email" class="form-control" id="ccEmail" name="ccEmail"
30+
aria-describedby="emailHelp" placeholder="pat@example.com" required
31+
<small id="emailHelp" class="form-text text-muted">The email for the cc recipient must be different from the signer's email.</small>
32+
</div>
33+
<div class="form-group">
34+
<label for="ccName">CC Name</label>
35+
<input type="text" class="form-control" id="ccName" placeholder="Pat Johnson" name="ccName"
36+
required>
37+
</div>
38+
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
39+
<button type="submit" class="btn btn-docu">Submit</button>
40+
</form>
41+
42+
<% include ../../partials/examplesFoot %>

views/pages/index_esignature_examples.ejs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,15 @@ then applies the brand to it. <a target='_blank'
301301
API method used:
302302
<a target="_blank" href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>.
303303
</p>
304+
305+
<h2>Responsive signing</h2>
306+
307+
<h4 id="example038">38. <a href="eg038">Signable HTML document</a></h4>
308+
<p>
309+
Demonstrates how to create an HTML document for responsive signing.
310+
<p>
311+
API methods used:
312+
<a target="_blank" href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>,
313+
<a target='_blank'
314+
href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/">EnvelopeViews::createRecipient</a>.
315+
</p>

0 commit comments

Comments
 (0)