Knocks/BackEnd/Knoks.Framework/Services/DocuSigneSignatureService.cs

168 lines
7.4 KiB
C#

using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
namespace Knoks.Framework.Services
{
public class DocuSigneSignatureService : ISignatureService, IDisposable
{
public string ServiceProvider { get { return "DocuSign"; } }
public string Version { get { return "1.0.0"; } }
private readonly ILogger<DocuSigneSignatureService> _logger;
private readonly DocuSigneSignatureSettings _settings;
private readonly HttpClient _httpClient;
private string _baseUrl;
public DocuSigneSignatureService(ILogger<DocuSigneSignatureService> logger, DocuSigneSignatureSettings settings)
{
_logger = logger;
_settings = settings;
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(settings.EndpointUrl);
_httpClient.DefaultRequestHeaders.Add("X-DocuSign-Authentication", JsonConvert.SerializeObject(new
{
Username = settings.Username,
Password = settings.Password,
IntegratorKey = settings.IntegratorKey
}));
}
public async Task<string> GetBaseUrl()
{
try
{
var res = await _httpClient.GetAsync("login_information");
var jsonResult = await res.Content.ReadAsStringAsync();
return JObject.Parse(jsonResult).SelectToken("loginAccounts[0].baseUrl").Value<string>();
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
throw;
}
}
public async Task<SignatureEntityOutput> RequestSignature(SignatureEntityInput signatureEntityInput, object settings = null)
{
try
{
if (signatureEntityInput.ServiceProvider != ServiceProvider)
throw new InvalidOperationException($"Invalid {nameof(signatureEntityInput.ServiceProvider)} value '{signatureEntityInput.ServiceProvider}'. an expected one is '{ServiceProvider}'");
if (signatureEntityInput.Version != Version)
throw new InvalidOperationException($"Invalid {nameof(signatureEntityInput.Version)} value '{signatureEntityInput.Version}'. an expected one is '{Version}'");
var docuSignEnvelope = signatureEntityInput.Input as JObject;
if (docuSignEnvelope == null)
throw new InvalidOperationException($"Invalid {nameof(signatureEntityInput.Input)} type '{signatureEntityInput.Input.GetType().FullName}'. an expected one is '{typeof(JObject).FullName}'");
var properties = (JObject)settings;
if (settings != null)
foreach (var item in (JObject)properties["JsonPlacement"])
((JProperty)docuSignEnvelope.SelectToken(item.Key).Parent).Value = item.Value;
await SetEventNotificationUrl(docuSignEnvelope, properties["LoanRequestAndUserUid"]?.Value<string>());
if (_baseUrl == null)
_baseUrl = await GetBaseUrl();
return new SignatureEntityOutput
{
ServiceProvider = ServiceProvider,
Version = Version,
Output = (await SendPostRequest(_baseUrl + "/envelopes", docuSignEnvelope)).ContentObject
};
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
throw;
}
}
//Install and run "ngrok agent" on your local machine for redirect webhoock to localhost.
//Additional info on https://ngrok.com/
//Command example when application running on IIS or IIS Express: ngrok http 52281 -host-header="localhost:52281"
//Cammand example when application running as self hosted hosted on 5000 port: ngrok http 5000
private async Task SetEventNotificationUrl(JObject jObj, string loanRequestAndUserUid)
{
if (jObj.SelectToken("eventNotification.url") == null)
return;
var uri = new Uri(_settings.EventNotificationUrl, UriKind.Absolute);
if (uri.Host.Equals("ngrok", StringComparison.OrdinalIgnoreCase))
{
using (var httpClient = new HttpClient())
{
var res = JObject.Parse(await httpClient.GetStringAsync("http://127.0.0.1:4040/api/tunnels"));
var ngrokUrl = res["tunnels"].Where(item => item["proto"].Value<string>() == uri.Scheme).Single()["public_url"].Value<string>();
uri = new Uri(ngrokUrl + uri.PathAndQuery, UriKind.Absolute);
}
}
jObj["eventNotification"]["url"] = uri.ToString().Replace($"{{{nameof(loanRequestAndUserUid)}}}", loanRequestAndUserUid ?? string.Empty);
}
public async Task<JObject> GetRecipientViewUrl(IDictionary<string, string> properties)
{
return (await SendPostRequest(_baseUrl + $"/envelopes/{properties["envelopeId"]}/views/recipient", JsonConvert.SerializeObject(new
{
authenticationMethod = properties["authenticationMethod"],
clientUserId = properties["clientUserId"],
email = properties["email"],
recipientId = properties["recipientId"],
userName = properties["name"]
}))).ContentObject;
}
private class SendPostResult
{
public HttpRequestMessage RequestMessage;
public HttpResponseMessage ResponseMessage;
public string ContentString;
public JObject ContentObject;
public HttpStatusCode StatusCode { get { return ResponseMessage.StatusCode; } }
}
private async Task<SendPostResult> SendPostRequest(string url, object body, HttpStatusCode expectedStatusCode = HttpStatusCode.Created, bool throwExceptionOnUnexpectedStatusCode = true)
{
//- request envelope --
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent(body.ToString(), Encoding.UTF8, "application/json");
//- response envelope --
var responce = await _httpClient.SendAsync(request);
var contentString = await responce.Content.ReadAsStringAsync();
var contentObject = JObject.Parse(await responce.Content.ReadAsStringAsync());
if (throwExceptionOnUnexpectedStatusCode && responce.StatusCode != HttpStatusCode.Created)
{
throw new InvalidOperationException(new JObject
{
["HttpStatusCode"] = (int)responce.StatusCode,
["Content"] = contentObject
}.ToString());
}
return new SendPostResult { RequestMessage = request, ResponseMessage = responce, ContentString = contentString, ContentObject = contentObject };
}
public void Dispose()
{
if (_httpClient != null)
_httpClient.Dispose();
}
}
}