Call Actions using Dynamics CRM Web API with Output Parameter


Let me share one scenario where I was working on payment gateway integration with Dynamics CRM . We were redirecting to payment page once user click on ribbon button and redirect to CRM on successful payment. Since we need to pass secure hash as a part of payment URL which is generated using secret key and some security sequence string. Since secret key is secure information so we should not pass to JavaScript so we decided to create Actions which will take some input and generate the Secure Hash using algorithm(SHA-256 HMAC) and return to output parameters.

So mainly 3 steps involve in complete requirement.
1. Create Actions with Input and Output parameter
2. Create Web API to Call Actions to get Secure Hash which will be used to pass as parameter to payment gateway.
3. Call JavaScript function from ribbon button which internally call Web API.

Here is my Actions with input and output parameters

Below is my Plugin code which I registered on “mc_GetSecureHash” message for custom entity called “mc_receipts”.

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace MC.CRM.PlugIns
{
public class GetSecureHash : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
string vendorName = string.Empty;
string merchantId = string.Empty;
string accessCode = string.Empty;
string secretKey = string.Empty;
string transactionType = string.Empty;
string sequenceStringForHash = string.Empty;
string secureHash = string.Empty;

// Obtain the execution context service from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

// Obtain the tracing service from the service provider.
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

// Obtain the Organization Service factory service from the service provider
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

// Use the factory to generate the Organization Service.
IOrganizationService service = factory.CreateOrganizationService(context.UserId);

try
{
if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is EntityReference)
{
EntityReference receipts = (EntityReference)context.InputParameters[“Target”];
sequenceStringForHash = (string)context.InputParameters[“sequencestring”];

vendorName = (string)context.InputParameters[“vendorname”];

}

EntityCollection paymentGatewaySettings = GetPaymentGatewayInformtion(service, vendorName);
if (paymentGatewaySettings.Entities.Count > 0)
{
Entity paymentSettings = paymentGatewaySettings.Entities[0];
if (paymentSettings.Attributes.Contains(“mc_merchantid”))
{
merchantId = paymentSettings.Attributes[“mc_merchantid”].ToString();
}
if (paymentSettings.Attributes.Contains(“mc_secretkey”))
{
secretKey = paymentSettings.Attributes[“mc_secretkey”].ToString();
}
if (paymentSettings.Attributes.Contains(“mc_accesscode”))
{
accessCode = paymentSettings.Attributes[“mc_accesscode”].ToString();
}

secureHash = GenerateSHA512String(secretKey, sequenceStringForHash);

context.OutputParameters[“securehash”] = secureHash;
}
}
catch (FaultException<OrganizationServiceFault> e)
{
throw new InvalidPluginExecutionException(e.Message);
}

}

public string GenerateSHA512String(string secretKey, string sequenceString)
{
byte[] hashmessage;
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secretKey);
byte[] messageBytes = encoding.GetBytes(sequenceString);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
hashmessage = hmacsha256.ComputeHash(messageBytes);
}
return Convert.ToBase64String(hashmessage);
}

private EntityCollection GetPaymentGatewayInformtion(IOrganizationService service, string venderName)
{

QueryByAttribute paymentGateway = new QueryByAttribute(“mc_paymentgatewaysettings”);
paymentGateway.ColumnSet = new ColumnSet(“mc_name”, “mc_merchantid”, “mc_secretkey”, “mc_accesscode”);
paymentGateway.Attributes.AddRange(“mc_name”);
paymentGateway.Values.AddRange(venderName);
EntityCollection paymentgatewaysettings = service.RetrieveMultiple(paymentGateway);

return paymentgatewaysettings;

}
}
}

Then finally Web API which calling the above Plugins

function GetSecureHash(requestStringForHash) {
debugger;

try
{
var secureHash;
var vendorName = “<>”;
var Id = Xrm.Page.data.entity.getId().substring(1, 37);
console.log(“Receipt Id:” + Id);
var data = {
“receiptid”: Id,
“vendorname”: vendorName,
“sequencestring”: requestStringForHash
};
var clientURL = Xrm.Page.context.getClientUrl();
var req = new XMLHttpRequest();

req.open(“POST”, clientURL + “/api/data/v8.2/mc_receiptses(” + Id + “)/Microsoft.Dynamics.CRM.mc_GetSecureHash”, false);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
var receipts = JSON.parse(this.response);
secureHash = receipts.securehash;
}
else {
var error = JSON.parse(this.response).error;
console.log(error.message);
}
}
};
req.send(JSON.stringify(data));
console.log(“secureHash: ” + secureHash);

}
catch (e) {
console.error(e.message);
}
return secureHash;

}

Using this Web API, we got secure hash to build the complete payment URL and then redirect to payment gateway.

Some reference URL for Action and Web API for more details
https://community.dynamics.com/crm/b/crminogic/archive/2018/01/04/fixed-executing-action-with-complex-output-parameters-through-web-api-in-dynamics-365-v9-0
Invoke your Custom Action from Dynamics CRM Web API–Dynamics CRM 2016

Hope this help you.

Retrieve Data Using Web API and FetchXml


Sharing sample code to retrieve data using Web API and using FetchXml to build the query instead of oData. In this example, I am retrieving contact based on email/mobile no and calling function form an entity’s ribbon button to validate duplicate record.

function ValidateContact() {
var mobilePhone = Xrm.Page.getAttribute(“mc_contactnumber”).getValue();
var email = Xrm.Page.getAttribute(“emailaddress”).getValue();
var clientURL = Xrm.Page.context.getClientUrl();
var fetchContact = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>” +
” <entity name=’contact’>” +
” <attribute name=’fullname’ />” +
” <attribute name=’emailaddress1′ />” +
” <attribute name=’mc_mobilephone’ />” +
” <attribute name=’createdon’ />” +
” <attribute name=’ownerid’ />” +
” <order attribute=’fullname’ descending=’false’ />” +
” <filter type=’and’>” +
” <filter type=’or’>” +
” <condition attribute=’mc_mobilephone’ operator=’eq’ value='” + mobilePhone + “‘ />” +
” <condition attribute=’emailaddress1′ operator=’eq’ value='” + email + “‘ />” +
” </filter>” +
” <condition attribute=’statecode’ operator=’eq’ value=’0′ />” +
” </filter>” +
” </entity>” +
“</fetch>”;

var encodedFetchXml = encodeURI(fetchContact);
var reqURL = clientURL + “/api/data/v8.2/contacts?fetchXml=” + encodedFetchXml;
var req = new XMLHttpRequest();
req.open(“GET”, reqURL, false);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);
//req.setRequestHeader(“Prefer”, “odata.include-annotations=\”*\””);
req.setRequestHeader(“Prefer”, “odata.include-annotations=\”OData.Community.Display.V1.FormattedValue\””);
req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null;
if (this.status == 200) {
var data = JSON.parse(this.response);
if (data.value.length == 0) {
alert(“There is no matching contact found in exiting contact list.”);
}
else if (data.value.length == 1) {
alert(“Duplicate Contact Record Found: Another Contact with the same detail already exists in the system which is created on: ” + data.value[0][‘createdon@OData.Community.Display.V1.FormattedValue’] + ” by ” + data.value[0][‘_ownerid_value@OData.Community.Display.V1.FormattedValue’] + “.”);
}
else {
alert(“There are more than one matching contact record found.”);
}
}
else {
var error = JSON.parse(this.response).error;
alert(error.message);
}
}
};
req.send();
}

To use FetchXml, we need to format FetchXml in usable Web API service endpoint format. We do this by storing the FetchXML in a variable and encoding the string with the encodeURI function native to JavaScrip as below.
var encodedFetchXml = encodeURI(fetchContact);

Another point, if you want formatted values to be included, then you need to set request header like below(This is mainly used to get Lookup, OptionSet, DateTime specific formatted value like lookup text value).
req.setRequestHeader(“Prefer”, “odata.include-annotations=\”*\””);
OR
req.setRequestHeader(“Prefer”, “odata.include-annotations=\”OData.Community.Display.V1.FormattedValue\””);

Please refer the below URL for details.
https://community.dynamics.com/crm/b/mscrmcustomization/archive/2016/10/18/ms-crm-2016-web-api-operations-retrieve-single-or-multiple-records

http://himbap.com/blog/?p=2012

%d bloggers like this: