C# DocuSign API:在 Docusign 上拉取服务器模板而不是本地模板文档

C# DocuSign API: Pulling server template instead of local template document on Docusign

我目前正在构建一个可用的 C# 项目,但我想从记录的服务器模板(托管在 docusign.com)而不是文档的本地模板副本(在我的计算机)。

我正在使用快速入门 C# 代码,但在尝试使其正常工作并用模板 ID 序列替换“World_Wide_Corp_fields.pdf”默认文件时卡住了:

更新:使用 Code-Samples-CSharp 中的 eg001 C# 代码作为基础(而不是快速入门 C# 代码)。

下面是 eg001 与 eg009 的组合,因此有一个使用服务器模板的嵌入式工作流程。

namespace eg_03_csharp_auth_code_grant_core.Views
{
    [Route("eg001")]
    public class Eg001EmbeddedSigningController : EgController
    {
        private string dsPingUrl;
        private string signerClientId = "1000";
        private string dsReturnUrl;

        public Eg001EmbeddedSigningController(DSConfiguration config, IRequestItemsService requestItemsService)
            : base(config, requestItemsService)
        {
            dsPingUrl = config.AppUrl + "/";
            dsReturnUrl = config.AppUrl + "/dsReturn";
            ViewBag.title = "Embedded Signing Ceremony";
        }

        // ***DS.snippet.0.start
        private string DoWork(string signerEmail, string signerName,
            string accessToken, string basePath, string accountId, string templateId)
        {
            // Data for this method
            // signerEmail 
            // signerName
            // accessToken
            // basePath
            // accountId
            // templateId

            // dsPingUrl -- class global
            // signerClientId -- class global
            // dsReturnUrl -- class global

            // Step 1. Create the envelope definition
            EnvelopeDefinition envelope = MakeEnvelope(signerEmail, signerName, ccEmail, ccName, templateId);

            // Step 2. Call DocuSign to create the envelope                   
            var config = new Configuration(new ApiClient(basePath));
            config.AddDefaultHeader("Authorization", "Bearer " + accessToken);
            EnvelopesApi envelopesApi = new EnvelopesApi(config);
            EnvelopeSummary result = envelopesApi.CreateEnvelope(accountId, envelope);
            return result.EnvelopeId;
        }

            // Save for future use within the example launcher
            RequestItemsService.EnvelopeId = envelopeId;

            // Step 3. create the recipient view, the Signing Ceremony
            RecipientViewRequest viewRequest = MakeRecipientViewRequest(signerEmail, signerName);
            // call the CreateRecipientView API
            ViewUrl results1 = envelopesApi.CreateRecipientView(accountId, envelopeId, viewRequest);

            // Step 4. Redirect the user to the Signing Ceremony
            // Don't use an iFrame!
            // State can be stored/recovered using the framework's session or a
            // query parameter on the returnUrl (see the makeRecipientViewRequest method)
            string redirectUrl = results1.Url;
            return redirectUrl;
        }

        private RecipientViewRequest MakeRecipientViewRequest(string signerEmail, string signerName)
        {
            // Data for this method
            // signerEmail 
            // signerName
            // dsPingUrl -- class global
            // signerClientId -- class global
            // dsReturnUrl -- class global


            RecipientViewRequest viewRequest = new RecipientViewRequest();
            // Set the url where you want the recipient to go once they are done signing
            // should typically be a callback route somewhere in your app.
            // The query parameter is included as an example of how
            // to save/recover state information during the redirect to
            // the DocuSign signing ceremony. It's usually better to use
            // the session mechanism of your web framework. Query parameters
            // can be changed/spoofed very easily.
            viewRequest.ReturnUrl = dsReturnUrl + "?state=123";

            // How has your app authenticated the user? In addition to your app's
            // authentication, you can include authenticate steps from DocuSign.
            // Eg, SMS authentication
            viewRequest.AuthenticationMethod = "none";

            // Recipient information must match embedded recipient info
            // we used to create the envelope.
            viewRequest.Email = signerEmail;
            viewRequest.UserName = signerName;
            viewRequest.ClientUserId = signerClientId;

            // DocuSign recommends that you redirect to DocuSign for the
            // Signing Ceremony. There are multiple ways to save state.
            // To maintain your application's session, use the pingUrl
            // parameter. It causes the DocuSign Signing Ceremony web page
            // (not the DocuSign server) to send pings via AJAX to your
            // app,
            viewRequest.PingFrequency = "600"; // seconds
                                               // NOTE: The pings will only be sent if the pingUrl is an https address
            viewRequest.PingUrl = dsPingUrl; // optional setting

            return viewRequest;
        }

        private EnvelopeDefinition MakeEnvelope(string signerEmail, string signerName, string ccEmail, string ccName, string templateId)
        {
        // Data for this method
        // signerEmail 
        // signerName
        // ccEmail
        // ccName
        // signerClientId -- class global
        // Config.docPdf replaced with templateId

            EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition();
            envelopeDefinition.TemplateId = templateId;
            envelopeDefinition.EmailSubject = "Please sign this document";

            // The order in the docs array determines the order in the envelope
            envelopeDefinition.Documents = new List<Document> { doc1 };

            // Create a signer recipient to sign the document, identified by name and email
            // We set the clientUserId to enable embedded signing for the recipient
            // We're setting the parameters via the object creation
            TemplateRole signer1 = new TemplateRole
            {
                Email = signerEmail,
                Name = signerName,
                ClientUserId = signerClientId,
                RecipientId = "1"
                RoleName = "signer";
             
            TemplateRole cc1 = new TemplateRole();
                cc1.Email = ccEmail;
                cc1.Name = ccName;
                cc1.RoleName = "cc";

            // Create signHere fields (also known as tabs) on the documents,
            // We're using anchor (autoPlace) positioning
            //
            // The DocuSign platform seaches throughout your envelope's
            // documents for matching anchor strings.
            SignHere signHere1 = new SignHere
            {
                AnchorString = "/sn1/",
                AnchorUnits = "pixels",
                AnchorXOffset = "10",
                AnchorYOffset = "20"
            };
            // Tabs are set per recipient / signer
            Tabs signer1Tabs = new Tabs
            {
                SignHereTabs = new List<SignHere> { signHere1 }
            };
            signer1.Tabs = signer1Tabs;

            // Add the recipient to the envelope object
            Recipients recipients = new Recipients
            {
                Signers = new List<TemplateRole> { signer1 }
            };
            envelopeDefinition.Recipients = recipients;

            // Request that the envelope be sent by setting |status| to "sent".
            // To request that the envelope be created as a draft, set to "created"
            envelopeDefinition.Status = "sent";

            return envelopeDefinition;
        }
        // ***DS.snippet.0.end


        public override string EgName => "eg001";

        [HttpPost]
        public IActionResult Create(string signerEmail, string signerName)
        {
        // Data for this method
        // signerEmail 
        // signerName
        // dsPingUrl -- class global
        // signerClientId -- class global
        // dsReturnUrl -- class global
        var accessToken = RequestItemsService.User.AccessToken;
        var basePath = RequestItemsService.Session.BasePath + "/restapi";
        var accountId = RequestItemsService.Session.AccountId;
        var templateId = RequestItemsService.TemplateId;


        // Check the token with minimal buffer time.
        bool tokenOk = CheckToken(3);
            if (!tokenOk)
            {
                // We could store the parameters of the requested operation 
                // so it could be restarted automatically.
                // But since it should be rare to have a token issue here,
                // we'll make the user re-enter the form data after 
                // authentication.
                RequestItemsService.EgName = EgName;
                return Redirect("/ds/mustAuthenticate");
            }

            string redirectUrl = DoWork(signerEmail, signerName, accessToken, basePath, accountId, templateId);
            // Redirect the user to the Signing Ceremony
            return Redirect(redirectUrl);
        }
    }
}

参考资料

对于这种情况,还有一个代码示例。 Using a template to create an envelope. 这是 C# 代码的片段:

private EnvelopeDefinition MakeEnvelope(string signerEmail, string signerName, 
            string ccEmail, string ccName, string templateId)
        {
            // Data for this method
            // signerEmail 
            // signerName
            // ccEmail
            // ccName
            // templateId

            EnvelopeDefinition env = new EnvelopeDefinition();
            env.TemplateId = templateId;

            TemplateRole signer1 = new TemplateRole();
            signer1.Email = signerEmail;
            signer1.Name =  signerName;
            signer1.RoleName = "signer";

            TemplateRole cc1 = new TemplateRole();
            cc1.Email = ccEmail;
            cc1.Name = ccName;
            cc1.RoleName = "cc";

            env.TemplateRoles = new List<TemplateRole> { signer1, cc1 };
            env.Status = "sent";
            return env;
        }