如何让同一ClientId重新签署Docusign.Click弹性签署协议
关于DocuSign Click弹性签署协议重新签署的实现问题
问题背景
- 已实现基于DocuSign.Click的C#测试方案,支持通过Agreement-Id获取协议响应数据、通过Client-Id获取协议信息
- 需求:向同一ClientId发送同一弹性签署协议完成重新签署,要求不删除该ClientId的历史协议、不新建弹性模板
- 测试异常:修改传入模板的日期参数后,仅返回“已同意”响应,未生成新的协议链接
现有测试代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DocuSign.Click.Api; using DocuSign.Click.Client; using DocuSign.Click.Model; using DocuSign.Click.Client.Auth; using DocuSign.eSign.Model; using static DocuSign.Click.Api.AccountsApi; namespace DocuSignConsoleTester { internal class Program { public static void Main(string[] args) { string integrationKey = "xx..xx"; string userId = "xx..xx"; string accountId = "xx..xx"; string authServer = "account-d.docusign.com"; // for demo environment string accountBaseURI = "https://demo.docusign.net/clickapi"; // for demo environment string fullName = "xx..xx"; string email = "xyz@example.com"; string company = "xx..xx"; string title = "xx..xx"; string clickwrapId = "xx..xx"; string datenow = DateTime.Now.ToString(); string access_token = GetJwtToken(integrationKey, userId, accountId, authServer); Console.WriteLine("Acess_Token: [" + access_token + "]"); Console.WriteLine("\r\r"); AccountsApi clickAccountApi = BuildHeader(accountBaseURI, access_token); UserAgreementRequest userAgreementRequest = BuildUpdateClickwrapHasAgreedRequest(fullName, email, company, title, datenow); ApiResponse<UserAgreementResponse> apiAgreement = CreateAgreement(clickAccountApi, accountId, clickwrapId, userAgreementRequest); string AgreedOn = apiAgreement.Data.AgreedOn.ToString(); string agreementId = apiAgreement.Data.AgreementId; string agreementURL = apiAgreement.Data.AgreementUrl; string clientUserid = apiAgreement.Data.ClientUserId; string createdOn = apiAgreement.Data.CreatedOn.ToString(); UserAgreementResponse AgreementInfo = GetAgreementInfo(clickAccountApi, accountId, clickwrapId, agreementId); List<UserAgreementResponse> ClientAgreementList = GetClientAgreementInfo(clickAccountApi, accountId, clickwrapId, agreementId, clientUserid); Console.WriteLine("API response data: "); Console.WriteLine(apiAgreement.Data.ToString()); Console.WriteLine("\r\r"); Console.WriteLine("Agreement response data: "); Console.WriteLine(AgreementInfo.ToString()); Console.WriteLine("\r\r"); Console.WriteLine("Client agreement data: "); foreach (UserAgreementResponse a in ClientAgreementList) { Console.WriteLine(a.ToString()); Console.WriteLine("\r"); } Console.WriteLine("\r\r"); Console.WriteLine(" End of test run "); } public static string GetJwtToken(string clientId, string userId, string accountId, string authServer) { string oauthBasePath = authServer; int expiresInHours = 1; List<string> scopes = new List<string>() { "signature impersonation click.manage click.send" }; DocuSignClient dsClient = new DocuSignClient(authServer); string rsaKeyBase64 = @"xx..xx=="; var rsaKeyBytes = Convert.FromBase64String(rsaKeyBase64); OAuth.OAuthToken token = dsClient.RequestJWTUserToken(clientId, userId, oauthBasePath, rsaKeyBytes, expiresInHours, scopes); return token.access_token.ToString(); } public static AccountsApi BuildHeader(string basePath, string accessToken) { var dsClient = new DocuSignClient(basePath); dsClient.Configuration.DefaultHeader.Add("Authorization", "Bearer " + accessToken); var clickAccountApi = new AccountsApi(dsClient); return clickAccountApi; } public static UserAgreementRequest BuildUpdateClickwrapHasAgreedRequest(string fullName, string email, string company, string title, string date) { var userAgreementRequest = new UserAgreementRequest { DocumentData = new Dictionary<string, string>() }; userAgreementRequest.DocumentData.Add("fullName", fullName); userAgreementRequest.DocumentData.Add("email", email); userAgreementRequest.DocumentData.Add("company", company); userAgreementRequest.DocumentData.Add("title", title); userAgreementRequest.DocumentData.Add("date", date); userAgreementRequest.ClientUserId = email; return userAgreementRequest; } public static ApiResponse<UserAgreementResponse> CreateAgreement(AccountsApi clickAccountApi, string accountId, string clickwrapId, UserAgreementRequest userAgreementRequest) { ApiResponse<UserAgreementResponse> response = clickAccountApi.CreateHasAgreedWithHttpInfo(accountId, clickwrapId, userAgreementRequest); if (response.StatusCode == 201) { return response; } else { response.Data.AgreementUrl = "Already Agreed"; return response; } } public static UserAgreementResponse GetAgreementInfo(AccountsApi clickAccountApi, string accountId, string clickwrapId, string agreementId) { UserAgreementResponse response = clickAccountApi.GetAgreement(accountId, clickwrapId, agreementId); return response; } public static List<UserAgreementResponse> GetClientAgreementInfo(AccountsApi clickAccountApi, string accountId, string clickwrapId, string agreementId, string clientId) { GetClickwrapAgreementsOptions gcao = new GetClickwrapAgreementsOptions(); gcao.clientUserId = clientId; ClickwrapAgreementsResponse acctResponse = clickAccountApi.GetClickwrapAgreements(accountId, clickwrapId, gcao); List<UserAgreementResponse> usrAgreements = acctResponse.UserAgreements.ToList(); return usrAgreements; } } }
解决方案(无需删除历史协议/新建模板)
核心逻辑
DocuSign Click的CreateHasAgreed接口默认会检查ClientUserId与Clickwrap的关联状态,若已存在有效同意记录则直接返回已同意状态。要触发重新签署,需通过ForceNewAgreement参数强制生成新的同意记录。
具体修改
添加强制生成新协议的参数
修改BuildUpdateClickwrapHasAgreedRequest方法,新增ForceNewAgreement = true配置:public static UserAgreementRequest BuildUpdateClickwrapHasAgreedRequest(string fullName, string email, string company, string title, string date) { var userAgreementRequest = new UserAgreementRequest { DocumentData = new Dictionary<string, string>() }; userAgreementRequest.DocumentData.Add("fullName", fullName); userAgreementRequest.DocumentData.Add("email", email); userAgreementRequest.DocumentData.Add("company", company); userAgreementRequest.DocumentData.Add("title", title); userAgreementRequest.DocumentData.Add("date", date); userAgreementRequest.ClientUserId = email; // 强制生成新协议,保留历史记录 userAgreementRequest.ForceNewAgreement = true; return userAgreementRequest; }调整响应处理逻辑
设置ForceNewAgreement=true后,即使ClientUserId已有历史协议,接口也会返回201状态码并生成新的协议链接,可简化响应处理:public static ApiResponse<UserAgreementResponse> CreateAgreement(AccountsApi clickAccountApi, string accountId, string clickwrapId, UserAgreementRequest userAgreementRequest) { ApiResponse<UserAgreementResponse> response = clickAccountApi.CreateHasAgreedWithHttpInfo(accountId, clickwrapId, userAgreementRequest); // 强制生成新协议后,合法请求均会返回201,新链接在response.Data.AgreementUrl中 return response; }
关键说明
ForceNewAgreement参数是核心,它会在保留历史协议的前提下,为同一ClientUserId创建新的同意记录- 历史协议可通过
GetClickwrapAgreements接口正常查询 - 修改模板参数(如日期)配合该参数,会生成包含最新参数的新协议链接,用户签署后新增一条同意记录
内容的提问来源于stack exchange,提问作者DigDug
相关产品推荐
相关产品推荐

