I am trying to consume Royal Mail shipping API in my C# Console Application but I am stuck. When I make a call to the API, it says Invalid Request..
This is what I did so far
RoyalMailMessage.cs
class RoyalMailMessage : Message
{
private readonly Message message;
public RoyalMailMessage(Message message)
{
this.message = message;
}
public override MessageHeaders Headers
{
get
{
return this.message.Headers;
}
}
public override MessageProperties Properties
{
get
{
return this.message.Properties;
}
}
public override MessageVersion Version
{
get
{
return this.message.Version;
}
}
protected override void OnWriteStartBody(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
this.message.WriteBodyContents(writer);
}
protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
{
writer.WriteStartElement("s", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
writer.WriteAttributeString("xmlns", "v2", null, "http://www.royalmailgroup.com/api/ship/V2");
writer.WriteAttributeString("xmlns", "v1", null, "http://www.royalmailgroup.com/integration/core/V1");
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
}
}
RoyalMailMessageFormatter.cs
public class RoyalMailMessageFormatter : IClientMessageFormatter
{
private readonly IClientMessageFormatter formatter;
public RoyalMailMessageFormatter(IClientMessageFormatter formatter)
{
this.formatter = formatter;
}
public object DeserializeReply(Message message, object[] parameters)
{
return this.formatter.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
var message = this.formatter.SerializeRequest(messageVersion, parameters);
return new RoyalMailMessage(message);
}
}
RoyalMailIEndpointBehavior.cs
class RoyalMailIEndpointBehavior : IOperationBehavior
{
public RoyalMailIEndpointBehavior() { }
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
IClientMessageFormatter currentFormatter = proxy.Formatter;
proxy.Formatter = new RoyalMailMessageFormatter(currentFormatter);
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
try
{
using (var shippingService = new shippingAPIPortTypeClient())
{
shippingService.ClientCredentials.UserName.UserName = "xxxx";
shippingService.ClientCredentials.UserName.Password = "xxxxx";
foreach (OperationDescription od in shippingService.Endpoint.Contract.Operations)
{
od.Behaviors.Add(new RoyalMailIEndpointBehavior());
}
var createShipment = new createShipmentRequest()
{
integrationHeader = new integrationHeader()
{
dateTime = DateTime.Now,
dateTimeSpecified = true,
debugFlag = false,
debugFlagSpecified = false,
identification = new identificationStructure()
{
applicationId = "xxxx",
endUserId = "Sandra",
intermediaryId = "null",
transactionId = "123456789"
},
performanceFlag = false,
performanceFlagSpecified = false,
testFlag = false,
testFlagSpecified = false,
version = 1,
versionSpecified = false
},
requestedShipment = new requestedShipment()
{
bfpoFormat = new bFPOFormatType()
{
bFPOFormatCode = null,
},
customerReference = "",
departmentReference = "",
}
};
shippingService.createShipment(null, createShipment);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="shippingAPISoapBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://api.royalmail.com/shipping/onboarding" binding="basicHttpBinding"
bindingConfiguration="shippingAPISoapBinding" contract="ShippingService.shippingAPIPortType"
name="shippingAPIPort" behaviorConfiguration="CustomBehavior" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="RM10001815" x509FindType="FindBySubjectName"
storeLocation="CurrentUser" storeName="My" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Now, when I make a call to the API, it says "Invalid Request"..I am not sure if I missing anything, may be adding credentials in Soap Envelop header like below?
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>xxxx</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">xxxx</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">xWstjXG0iUxbv3NH/fX+kw==</wsse:Nonce>
<wsu:Created>2014-08-16T15:29:42</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Firstly you are missing the security header as you have already identified, plus a whole host of other fields in your createShipment request such as address, service code etc. I would strongly suggest using fiddler to capture your SOAP requests and responses, they will give you a lot more insight into what is happening. You can also compare the requests you are generating with the example requests provided by royal mail onboarding.
Looking at your code, you are not attaching the security tokens (the wsse) which needs to be unique for every request you make (The nonce token that is). You are also missing a variety of other required fields for the createShipemt request such as address, service code and type etc.
I had to attach the certificate and key to the request to make it work as well. Below are some fragments of the code I created to make this work, it's not a copy paste solution but better than anything else you will find out there with regards to Royal Mail and C# and should point you in the right direction.
Please note, I have config class which loads a lot of the settings from an sqlite database (not posted). The values for the createShipment request are coming from a form (not posted) which is pre-populated with the data but allows the user in our warehouse to alter and adjust accordingly. You have already made use of my custom message formatter example from post (C# WCF (Royal Mail SOAP API) Declare Namespace In Header) to handle the namespace issue. Royal Mail API is not easy to implement in C#, took me nearly 2 days to get a valid request and response and as I say, you really need to capture the requests and responses to work out what is going on.
private X509Certificate2 certificate;
private Config config;
public RoyalMail() {
// Load The Config
config = new Config();
config.loadConfig();
// Load The SSL Certificate (Check The File Exists)
String certificatePath = (Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + #"\" + config.GetCertificateName());
if (!System.IO.File.Exists(certificatePath))
{
throw new Exception(#"The Royal Mail Certificate Is Missing From The Plugins Directory. Please Place The File " + config.GetCertificateName() + " In The Same Directory As The Plugin DLL File & Relaunch FileMaker.\n\n" + certificatePath);
}
certificate = new X509Certificate2(certificatePath, config.GetCertificatePassword());
// Check It's In The Certificate
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
if (!store.Certificates.Contains(certificate))
{
store.Add(certificate);
MessageBox.Show("Certificate Was Installed Into Computer Trust Store");
}
store.Close();
}
/*
*
* SOAP Service & Methods
*
*/
private shippingAPIPortTypeClient GetProxy()
{
BasicHttpBinding myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
myBinding.MaxReceivedMessageSize = 2147483647;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(new Uri(config.GetEndpointURL()), EndpointIdentity.CreateDnsIdentity("api.royalmail.com"), new AddressHeaderCollection()));
shippingClient.ClientCredentials.ClientCertificate.Certificate = certificate;
foreach (OperationDescription od in shippingClient.Endpoint.Contract.Operations)
{
od.Behaviors.Add(new RoyalMailIEndpointBehavior());
}
return shippingClient;
}
private SecurityHeaderType GetSecurityHeaderType()
{
SecurityHeaderType securityHeader = new SecurityHeaderType();
DateTime created = DateTime.Now;
string creationDate;
creationDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
string nonce = nonce = (new Random().Next(0, int.MaxValue)).ToString();
byte[] hashedPassword;
hashedPassword = GetSHA1(config.GetPassword());
string concatednatedDigestInput = string.Concat(nonce, creationDate, Encoding.Default.GetString(hashedPassword));
byte[] digest;
digest = GetSHA1(concatednatedDigestInput);
string passwordDigest;
passwordDigest = Convert.ToBase64String(digest);
string encodedNonce;
encodedNonce = Convert.ToBase64String(Encoding.Default.GetBytes(nonce));
XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild())
{
writer.WriteStartDocument();
writer.WriteStartElement("Security");
writer.WriteStartElement("UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteElementString("Username", config.GetUsername());
writer.WriteElementString("Password", passwordDigest);
writer.WriteElementString("Nonce", encodedNonce);
writer.WriteElementString("Created", creationDate);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
doc.DocumentElement.RemoveAllAttributes();
System.Xml.XmlElement[] headers = doc.DocumentElement.ChildNodes.Cast<XmlElement>().ToArray<XmlElement>();
securityHeader.Any = headers;
return securityHeader;
}
private integrationHeader GetIntegrationHeader()
{
integrationHeader header = new integrationHeader();
DateTime created = DateTime.Now;
String createdAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
header.dateTime = created;
header.version = Int32.Parse(config.GetVersion());
header.dateTimeSpecified = true;
header.versionSpecified = true;
identificationStructure idStructure = new identificationStructure();
idStructure.applicationId = config.GetApplicationID();
string nonce = nonce = (new Random().Next(0, int.MaxValue)).ToString();
idStructure.transactionId = CalculateMD5Hash(nonce + createdAt);
header.identification = idStructure;
return header;
}
private static byte[] GetSHA1(string input)
{
return SHA1Managed.Create().ComputeHash(Encoding.Default.GetBytes(input));
}
public string CalculateMD5Hash(string input)
{
// step 1, calculate MD5 hash from input
MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = md5.ComputeHash(inputBytes);
// step 2, convert byte array to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}
/*
* Check Response Footer For Errors & Warnings From Service
* If Error Return True So We Can Inform Filemaker Of Error
* Ignore Warnings For Now
*
*/
private bool checkErrorsAndWarnings(integrationFooter integrationFooter)
{
if (integrationFooter != null)
{
if (integrationFooter.errors != null && integrationFooter.errors.Length > 0)
{
errorDetail[] errors = integrationFooter.errors;
for (int i = 0; i < errors.Length; i++)
{
errorDetail error = errors[i];
MessageBox.Show("Royal Mail Request Error: " + error.errorDescription + ". " + error.errorResolution, "Royal Mail Request Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
if (errors.Length > 0)
{
return true;
}
}
if (integrationFooter.warnings != null && integrationFooter.warnings.Length > 0)
{
warningDetail[] warnings = integrationFooter.warnings;
for (int i = 0; i < warnings.Length; i++)
{
warningDetail warning = warnings[i];
//MessageBox.Show("Royal Mail Request Warning: " + warning.warningDescription + ". " + warning.warningResolution, "Royal Mail Request Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
}
}
}
return false;
}
/*
* Show Message Box With SOAP Error If We Receive A Fault Code Back From Service
*
*/
private void showSoapException(FaultException e)
{
MessageFault message = e.CreateMessageFault();
XmlElement errorDetail = message.GetDetail<XmlElement>();
XmlNodeList errorDetails = errorDetail.ChildNodes;
String fullErrorDetails = "";
for (int i = 0; i < errorDetails.Count; i++)
{
fullErrorDetails += errorDetails.Item(i).Name + ": " + errorDetails.Item(i).InnerText + "\n";
}
MessageBox.Show("An Error Occured With Royal Mail Service: " + message.Reason.ToString() + "\n\n" + fullErrorDetails, "Royal Mail SOAP Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
public createShipmentResponse SendCreateShipmentRequest(CreateShipmentForm shippingForm)
{
shippingAPIPortTypeClient client = GetProxy();
try
{
createShipmentRequest request = new createShipmentRequest();
request.integrationHeader = GetIntegrationHeader();
requestedShipment shipment = new requestedShipment();
// Shipment Type Code (Delivery or Return)
referenceDataType shipmentType = new referenceDataType();
shipmentType.code = shippingForm.GetShippingType();
shipment.shipmentType = shipmentType;
// Service Occurence (Identifies Agreement on Customers Account) Default to 1. Not Required If There Is There Is Only 1 On Account
shipment.serviceOccurrence = config.GetServiceOccurance();
// Service Type Code (1:24H 1st Class, 2: 48H 2nd Class, D: Special Delivery Guaranteed, H: HM Forces (BFPO), I: International, R: Tracked Returns, T: Tracked Domestic)
referenceDataType serviceType = new referenceDataType();
serviceType.code = shippingForm.GetServiceType().GetServiceTypeCode();
shipment.serviceType = serviceType;
// Service Offering (See Royal Mail Service Offering Type Codes. Too Many To List)
serviceOfferingType serviceOfferingTypeContainer = new serviceOfferingType();
referenceDataType serviceOffering = new referenceDataType();
serviceOffering.code = shippingForm.GetServiceOffering().GetCode();
serviceOfferingTypeContainer.serviceOfferingCode = serviceOffering;
shipment.serviceOffering = serviceOfferingTypeContainer;
// Service Format Code
serviceFormatType serviceFormatTypeContainer = new serviceFormatType();
referenceDataType serviceFormat = new referenceDataType();
serviceFormat.code = shippingForm.GetServiceFormat().GetFormat();
serviceFormatTypeContainer.serviceFormatCode = serviceFormat;
shipment.serviceFormat = serviceFormatTypeContainer;
// Shipping Date
shipment.shippingDate = shippingForm.GetShippingDate();
shipment.shippingDateSpecified = true;
// Signature Required (Only Available On Tracked Services)
if (shippingForm.IsSignatureRequired())
{
shipment.signature = true;
}
else
{
shipment.signature = false;
// Leave In Safe Place (Available On Tracked Non Signature Service Offerings)
shipment.safePlace = shippingForm.GetSafePlaceText();
}
shipment.signatureSpecified = true;
// Sender Reference Number (e.g. Invoice Number or RA Number)
shipment.senderReference = shippingForm.GetInvoiceNumber();
/*
* Service Enhancements
*/
List<serviceEnhancementType> serviceEnhancements = new List<serviceEnhancementType>();
List<dataObjects.ServiceEnhancement> selectedEnhancements = shippingForm.GetServiceEnhancements();
for (int i = 0; i < selectedEnhancements.Count; i++)
{
serviceEnhancementType enhancement = new serviceEnhancementType();
referenceDataType enhancementCode = new referenceDataType();
enhancementCode.code = selectedEnhancements.ElementAt(i).GetEnhancementType().ToString();
enhancement.serviceEnhancementCode = enhancementCode;
serviceEnhancements.Add(enhancement);
}
shipment.serviceEnhancements = serviceEnhancements.ToArray();
/*
* Recipient Contact Details
*/
contact recipientContact = new contact();
recipientContact.complementaryName = shippingForm.GetCompany();
recipientContact.name = shippingForm.GetName();
if(!shippingForm.GetEmailAddress().Equals("")) {
digitalAddress email = new digitalAddress();
email.electronicAddress = shippingForm.GetEmailAddress();
recipientContact.electronicAddress = email;
}
if(!shippingForm.GetMobileNumber().Equals("")) {
telephoneNumber tel = new telephoneNumber();
Regex phoneRegex = new Regex(#"[^\d]");
tel.telephoneNumber1 = phoneRegex.Replace(shippingForm.GetMobileNumber(), "");
tel.countryCode = "00" + shippingForm.GetCountry().GetDialingCode();
recipientContact.telephoneNumber = tel;
}
shipment.recipientContact = recipientContact;
/*
* Recipient Address
*
*/
address recipientAddress = new address();
recipientAddress.addressLine1 = shippingForm.GetAddressLine1();
recipientAddress.addressLine2 = shippingForm.GetAddressLine2();
recipientAddress.addressLine3 = shippingForm.GetAddressLine3();
recipientAddress.addressLine4 = shippingForm.GetCounty();
recipientAddress.postTown = shippingForm.GetTown();
countryType country = new countryType();
referenceDataType countryCode = new referenceDataType();
countryCode.code = shippingForm.GetCountry().getCountryCode();
country.countryCode = countryCode;
recipientAddress.country = country;
recipientAddress.postcode = shippingForm.GetPostCode();
recipientAddress.stateOrProvince = new stateOrProvinceType();
recipientAddress.stateOrProvince.stateOrProvinceCode = new referenceDataType();
shipment.recipientAddress = recipientAddress;
// Shipment Items
List<RoyalMailAPI.RoyalMailShippingAPI.item> items = new List<RoyalMailAPI.RoyalMailShippingAPI.item> ();
foreach(dataObjects.Item i in shippingForm.GetItems()) {
RoyalMailAPI.RoyalMailShippingAPI.item item = new RoyalMailAPI.RoyalMailShippingAPI.item();
item.numberOfItems = i.GetQty().ToString();
item.weight = new dimension();
item.weight.value = (float) (i.GetWeight() * 1000);
item.weight.unitOfMeasure = new unitOfMeasureType();
item.weight.unitOfMeasure.unitOfMeasureCode = new referenceDataType();
item.weight.unitOfMeasure.unitOfMeasureCode.code = "g";
items.Add(item);
}
if (shippingForm.GetServiceType().GetDescription().ToLower().Contains("international"))
{
internationalInfo InternationalInfo = new internationalInfo();
InternationalInfo.shipperExporterVatNo = "GB945777273";
InternationalInfo.documentsOnly = false;
InternationalInfo.shipmentDescription = "Invoice Number: " + shippingForm.GetInvoiceNumber();
InternationalInfo.invoiceDate = DateTime.Now;
InternationalInfo.termsOfDelivery = "EXW";
InternationalInfo.invoiceDateSpecified = true;
InternationalInfo.purchaseOrderRef = shippingForm.GetInvoiceNumber();
List<RoyalMailShippingAPI.parcel> parcels = new List<parcel>();
foreach (dataObjects.Item i in shippingForm.GetItems())
{
parcel Parcel = new parcel();
Parcel.weight = new dimension();
Parcel.weight.value = (float)(i.GetWeight() * 1000);
Parcel.weight.unitOfMeasure = new unitOfMeasureType();
Parcel.weight.unitOfMeasure.unitOfMeasureCode = new referenceDataType();
Parcel.weight.unitOfMeasure.unitOfMeasureCode.code = "g";
Parcel.invoiceNumber = shippingForm.GetInvoiceNumber();
Parcel.purposeOfShipment = new referenceDataType();
Parcel.purposeOfShipment.code = "31";
List<contentDetail> Contents = new List<contentDetail>();
foreach (RoyalMailAPI.dataObjects.ProductDetail product in i.GetProducts())
{
contentDetail ContentDetail = new contentDetail();
ContentDetail.articleReference = product.Sku;
ContentDetail.countryOfManufacture = new countryType();
ContentDetail.countryOfManufacture.countryCode = new referenceDataType();
ContentDetail.countryOfManufacture.countryCode.code = product.CountryOfManufacture;
ContentDetail.currencyCode = new referenceDataType();
ContentDetail.currencyCode.code = product.CurrencyCode;
ContentDetail.description = product.Name;
ContentDetail.unitQuantity = product.Qty.ToString();
ContentDetail.unitValue = Convert.ToDecimal(product.Price);
ContentDetail.unitWeight = new dimension();
ContentDetail.unitWeight.value = Convert.ToSingle(product.Weight * 1000);
ContentDetail.unitWeight.unitOfMeasure = new unitOfMeasureType();
ContentDetail.unitWeight.unitOfMeasure.unitOfMeasureCode = new referenceDataType();
ContentDetail.unitWeight.unitOfMeasure.unitOfMeasureCode.code = "g";
Contents.Add(ContentDetail);
}
//Parcel.contentDetails = Contents.ToArray();
parcels.Add(Parcel);
}
InternationalInfo.parcels = parcels.ToArray();
shipment.internationalInfo = InternationalInfo;
}
else
{
shipment.items = items.ToArray();
}
request.requestedShipment = shipment;
createShipmentResponse response = client.createShipment(GetSecurityHeaderType(), request);
// Show Errors And Warnings
checkErrorsAndWarnings(response.integrationFooter);
return response;
}
catch (TimeoutException e)
{
client.Abort();
MessageBox.Show("Request Timed Out: " + e.Message, "Request Timeout", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
catch (FaultException e)
{
client.Abort();
showSoapException(e);
}
catch (CommunicationException e)
{
client.Abort();
MessageBox.Show("A communication error has occured: " + e.Message + " - " + e.StackTrace, "Communication Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
catch (Exception e)
{
client.Abort();
MessageBox.Show(e.Message, "Royal Mail Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
return null;
}
Related
I'm trying to make a call to the HotelPropertyDescriptionLLSRQ Sabre Web Service using C#. I've gotten other APIs to work with similar code but I keep getting the same Client Validation Failed error, even after retrieving a valid security token.
I think I'm missing some small piece in the proxy model, which is below.
Search Criteria Object:
HotelSearchCriteria searchCriteria = new HotelSearchCriteria
{
hotelCode = "1191",
inDate = "8-22",
outDate = "8-25"
};
And then the proxy class:
public class HotelPropertyDescriptionReq
{
private HotelPropertyDescriptionService service;
private HotelPropertyDescriptionRQ req;
public HotelPropertyDescriptionRS response;
public string xmlResponse;
public bool searchPerformed = false;
public HotelPropertyDescriptionReq()
{
//parameterless constructor for serialization
}
public HotelPropertyDescriptionReq(SessionToken token, HotelSearchCriteria searchCriteria)
{
//argument validation - must have a location of some sort and some dates for search to work. Throw exception here if none found
if (searchCriteria.hotelCode == null && searchCriteria.cityCode == null)
{
//no search can take place if this is null...send back an empty response with searchPerformed == fals
throw new ArgumentException("Cannot search hotel availability without hotelCode or cityCode");
}
else if (searchCriteria.inDate == null || searchCriteria.outDate == null)
{
throw new ArgumentException("Cannot serach hotel availability without inDate and outDate");
}
//MessageHeader
MessageHeader mHeader = new MessageHeader();
PartyId[] pId = { new PartyId() };
pId[0].Value = "SWS";
From from = new From();
from.PartyId = pId;
To to = new To();
to.PartyId = pId;
mHeader.Action = "HotelPropertyDescriptionLLSRQ";
mHeader.version = "2.3.0";
mHeader.Service = new Service()
{
Value = mHeader.Action
};
mHeader.ConversationId = token.conversationID;
mHeader.CPAId = token.ipcc;
mHeader.From = from;
mHeader.To = to;
mHeader.MessageData = new MessageData()
{
Timestamp = DateTime.UtcNow.ToString(),
};
//Security
//Security sec = new Security();
Security1 sec = new Security1();
sec.BinarySecurityToken = token.securityToken;
//Service
service = new HotelPropertyDescriptionService();
service.MessageHeaderValue = mHeader;
//service.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap11;
service.Security = sec;
//request
req = new HotelPropertyDescriptionRQ();
req.AvailRequestSegment = new HotelPropertyDescriptionRQAvailRequestSegment();
req.AvailRequestSegment.GuestCounts = new HotelPropertyDescriptionRQAvailRequestSegmentGuestCounts();
req.AvailRequestSegment.GuestCounts.Count = "1";
req.AvailRequestSegment.HotelSearchCriteria = new HotelPropertyDescriptionRQAvailRequestSegmentHotelSearchCriteria();
req.AvailRequestSegment.HotelSearchCriteria.Criterion = new HotelPropertyDescriptionRQAvailRequestSegmentHotelSearchCriteriaCriterion();
req.AvailRequestSegment.HotelSearchCriteria.Criterion.HotelRef = new HotelPropertyDescriptionRQAvailRequestSegmentHotelSearchCriteriaCriterionHotelRef();
req.AvailRequestSegment.HotelSearchCriteria.Criterion.HotelRef.HotelCode = searchCriteria.hotelCode;
req.AvailRequestSegment.TimeSpan = new HotelPropertyDescriptionRQAvailRequestSegmentTimeSpan();
req.AvailRequestSegment.TimeSpan.Start = searchCriteria.inDate;
req.AvailRequestSegment.TimeSpan.End = searchCriteria.outDate;
string requestXML = Serializer.toXML(req);
string headerXML = Serializer.toXML(mHeader);
//send the request
try
{
response = service.HotelPropertyDescriptionRQ(req);
searchPerformed = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
}
Here is a working request - your payload looks a little bit diffrent then the one created by you. Just try to replicate this - need to set the PCC and securitycredentials:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:eb="http://www.ebxml.org/namespaces/messageHeader" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Header>
<eb:MessageHeader SOAP-ENV:mustUnderstand="1" eb:version="2.0">
<eb:From>
<eb:PartyId type="urn:x12.org:IO5:01">1212</eb:PartyId>
</eb:From>
<eb:To>
<eb:PartyId type="urn:x12.org:IO5:01">2323</eb:PartyId>
</eb:To>
<eb:CPAId>YOURPCCHERE</eb:CPAId>
<eb:ConversationId>XXX-dd74-4500-99d6-1e746b8876cc1507217090989</eb:ConversationId>
<eb:Service eb:type="OTA">HotelPropertyDescriptionLLSRQ</eb:Service>
<eb:Action>HotelPropertyDescriptionLLSRQ</eb:Action>
<eb:MessageData>
<eb:MessageId>1001</eb:MessageId>
<eb:Timestamp>2016-06-07T10:00:01</eb:Timestamp>
<eb:TimeToLive>2017-06-06T23:59:59</eb:TimeToLive>
</eb:MessageData>
</eb:MessageHeader>
<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/12/utility">
<wsse:BinarySecurityToken EncodingType="wsse:Base64Binary" valueType="String">YOURSECRETHERE</wsse:BinarySecurityToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<HotelPropertyDescriptionRQ xmlns="http://webservices.sabre.com/sabreXML/2011/10" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ReturnHostCommand="false" TimeStamp="2015-11-30T12:00:00-06:10" Version="2.3.0">
<AvailRequestSegment>
<GuestCounts Count="1"/>
<HotelSearchCriteria>
<Criterion>
<HotelRef HotelCode="0022426" UnitOfMeasure="KM"/>
</Criterion>
</HotelSearchCriteria>
<POS>
<Source>
<CompanyName Division="BER"/>
</Source>
</POS>
<TimeSpan End="04-24" Start="04-22" />
</AvailRequestSegment>
</HotelPropertyDescriptionRQ>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I am writing a plugin for Dynamics 2017 on premise using C#. The plugin is supposed to run synchronously post operation on the win of an opportunity. When I mark an opportunity as won, I receive the following error:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">VerifyCommitted - Transaction has not been committed</faultstring>
<detail>
<OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ActivityId>11be55c4-321a-4b84-b9b7-f2451579bc67</ActivityId>
<ErrorCode>-2147220910</ErrorCode>
<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
<Message>VerifyCommitted - Transaction has not been committed</Message>
<Timestamp>2018-07-05T18:37:53.9996395Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true"/>
<InnerFault i:nil="true"/>
<OriginalException i:nil="true"/>
<TraceText i:nil="true"/>
</OrganizationServiceFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
I've tried profiling the plugin so I can debug what is happening here, but it doesn't appear that the plugin is successfully profiling the action. Why am I receiving this error and why is my plugin not profiling correctly?
private void ExecutePostOpportunityWin(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
try
{
IPluginExecutionContext context = localContext.PluginExecutionContext;
Entity wonOpportunityEntity = context.InputParameters.Contains("OpportunityClose") ?
(Entity)context.InputParameters["OpportunityClose"] :
throw new InvalidPluginExecutionException("Unable to load OpportunityClose Input Parameter. Please try again in a few minutes. If the problem persists please contact IT Support.");
EntityReference wonOpportunityReference = wonOpportunityEntity.Attributes.Contains("opportunityid") ?
(EntityReference)wonOpportunityEntity.Attributes["opportunityid"] :
throw new InvalidPluginExecutionException("Unable to load opportunityid. Please try again in a few minutes. If the problem persists please contact IT Support.");
IOrganizationService service = localContext.OrganizationService;
if (wonOpportunityReference != null)
{
Opportunity wonOpportunity = (Opportunity)service.Retrieve(
"opportunity",
new Guid(wonOpportunityReference.Id.ToString()),
new ColumnSet(new String[] {
"customerid",
"ownerid",
"ccseq_salesleadid",
"description",
"ccseq_newcontract",
"ccseq_newclient",
"ccseq_clientid",
"ccseq_contractid",
"name"
})
);
if (wonOpportunity != null)
{
Boolean isNewClient = wonOpportunity.NewClient == true;
Boolean isNewContract = wonOpportunity.NewContract == true;
if (isNewClient && isNewContract)
{
Client newClient = CreateClient(wonOpportunity, service);
Contract newContract = CreateContract(wonOpportunity, newClient, service);
AssociateOpportunityToContract(wonOpportunity.Id, newContract.Id, service);
wonOpportunity.Client = newClient;
wonOpportunity.Contract = newContract;
wonOpportunity.Save(service);
}
else if (isNewClient && !isNewContract)
{
Client newClient = CreateClient(wonOpportunity, service);
AssociateOpportunityToContract(wonOpportunity.Id, wonOpportunity.Contract.Id, service);
wonOpportunity.Client = newClient;
wonOpportunity.Save(service);
}
else if (!isNewClient && isNewContract)
{
Entity entity = service.Retrieve(
"ccseq_client",
wonOpportunity.Client.Id,
new ColumnSet(new String[] { "ccseq_clientid", "ccseq_masterclientid" })
);
Client newClient = (Client)entity;
if (newClient.MasterClient != null)
{
entity = service.Retrieve(
"ccseq_client",
newClient.Id,
new ColumnSet(new String[] { "ccseq_clientid", "ccseq_masterclientid" })
);
newClient = (Client)entity;
}
Contract newContract = CreateContract(wonOpportunity, newClient, service);
AssociateOpportunityToContract(wonOpportunity.Id, newContract.Id, service);
wonOpportunity.Contract = newContract;
wonOpportunity.Save(service);
}
else if (!isNewClient && !isNewContract)
{
AssociateOpportunityToContract(wonOpportunity.Id, wonOpportunity.Contract.Id, service);
}
else
{
throw new InvalidPluginExecutionException("Unable to process Client and Contract Information. Please try again in a few minutes. If the problem persists please contact IT Support.");
}
}
else
{
throw new InvalidPluginExecutionException("Unable to load Opportunity. Please try again in a few minutes. If the problem persists please contact IT Support.");
}
}
else
{
throw new InvalidPluginExecutionException("Unable to load Opportunity. Please try again in a few minutes. If the problem persists please contact IT Support.");
}
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
private void AssociateOpportunityToContract(Guid opportunityID, Guid contractID, IOrganizationService service)
{
service.Associate(
"ccseq_contract",
contractID,
new Relationship("ccseq_ccseq_contract_opportunity_ContractID"),
new EntityReferenceCollection(
new EntityReference[] { new EntityReference("opportunity", opportunityID) }
)
);
}
private Contract CreateContract(Opportunity opportunity, Client clientGroup, IOrganizationService service)
{
Contract newContract = new Contract();
newContract.ApprovingPartner = opportunity.SalesLead;
newContract.Preparer = opportunity.SalesLead;
newContract.Description = opportunity.Topic;
newContract.Executed = false;
newContract.ContractStatus = Contract.eContractStatus.Draft;
newContract.ClientGroup = clientGroup;
newContract.Id = newContract.Save(service);
ContractLine newContractLine = new ContractLine();
newContractLine.Contract = newContract;
newContractLine.Id = newContractLine.Save(service);
ContractRevenue newContractRevenue = new ContractRevenue();
newContractRevenue.Contract = newContract;
newContractRevenue.Id = newContractRevenue.Save(service);
ContractRevenueByContractLine newContractRevenueByContractLine = new ContractRevenueByContractLine();
newContractRevenueByContractLine.ContractLine = newContractLine;
newContractRevenueByContractLine.ContractRevenue = newContractRevenue;
newContractRevenueByContractLine.Id = newContractRevenueByContractLine.Save(service);
ContractJob newContractJob = new ContractJob();
newContractJob.ContractLine = newContractLine;
newContractJob.Id = newContractJob.Save(service);
return newContract;
}
private Client CreateClient(Opportunity opportunity, IOrganizationService service)
{
Client newClient = new Client();
newClient.Name = opportunity.AssociationOrContact.Name;
newClient.Originator = opportunity.Originator;
newClient.Preparer = opportunity.SalesLead;
newClient.ClientPartner = opportunity.SalesLead;
newClient.ClientStatus = Client.eClientStatus.Draft;
if (opportunity.AssociationOrContact != null)
{
if (opportunity.AssociationOrContact.LogicalName == "account")
{
Association association = null;
association = (Association)service.Retrieve(
"account",
opportunity.AssociationOrContact.Id,
new ColumnSet(new String[]
{
"ownershipcode",
"industrycode",
"ccseq_fiscalyearendmonth",
"ccseq_naicscode",
"address1_line1",
"address1_line2",
"address1_line3",
"address1_city",
"address1_stateorprovince",
"address1_postalcode",
"address1_country"
})
);
if (association != null)
{
newClient.Association = association;
newClient.FiscalYearEndMonth = association.FiscalYearEndMonth != null ? (Client.eMonthOfYear)association.FiscalYearEndMonth : (Client.eMonthOfYear?)null;
//client.ClientType = (Client.eClientType)association.EntityType; // These two option sets are different
newClient.SameAsClientAddress = true;
newClient.Address1 = association.Address1_Street1;
newClient.Address2 = association.Address1_Street2;
newClient.Address3 = association.Address1_Street3;
newClient.City = association.Address1_City;
newClient.State = association.Address1_State_Province;
newClient.Zip = association.Address1_ZIP_PostalCode != null ? Convert.ToInt32(association.Address1_ZIP_PostalCode) : (int?)null;
newClient.Country = association.Address1_County;
newClient.BillingAddress1 = association.Address1_Street1;
newClient.BillingAddress2 = association.Address1_Street2;
newClient.BillingAddress3 = association.Address1_Street3;
newClient.BillingCity = association.Address1_City;
newClient.BillingState = association.Address1_State_Province;
newClient.BillingZip = association.Address1_ZIP_PostalCode != null ? Convert.ToInt32(association.Address1_ZIP_PostalCode) : (int?)null;
newClient.BillingCountry = association.Address1_County;
}
}
else
{
Contact contact = (Contact)service.Retrieve(
"contact",
opportunity.AssociationOrContact.Id,
new ColumnSet(new String[]
{
"firstname",
"lastname",
"emailaddress1"
})
);
if (contact != null)
{
newClient.Name = contact.LastName + " Household";
newClient.BillingContactName = contact.FirstName + " " + contact.LastName;
newClient.BillingContactEmail = contact.PrimaryEmail;
// Address Information doesn't quite line up
}
}
}
newClient.Id = newClient.Save(service);
return newClient;
}
Plugin Configuration
As #HenkvanBoeijen said in the comments on this question the issue was that I was trying to update the opportunity record that had been locked by the system. When I made this plugin a pre-operation plugin and set the values I needed in the target the error went away.
I'm trying to integrate the Royal Mail SOAP API into my .NET Code. I have followed the advice here Consume WCF Royal Mail API in c# Console Application and here C# WCF Namespaces Move To Header & Use NS Prefix.
I have created a custom IClientMessageFormatter to be able to attach the namespaces to the beginning of the soap envelope, but I still can't seem to get this to work. I keep receiving the following error. Could not establish trust relationship for the SSL/TLS secure channel with authority 'api.royalmail.com', and the inner exception is: The remote certificate is invalid according to the validation procedure.
I am using Visual Studio 13 and .Net version 3.5, I've tried numerous other versions but with no further progress. When I debug I can see that the normal message been passed into the RoyalMailMessage but when it runs OnWriteStartEnvelope I can't see any changes to the _message object. I've created a trace to see what soap request is been sent.
I have sent my XML request to Royal Mail support who validate that the reason it is failing is due to the namespaces not been declared in the envelope and the missing prefixes.
RoyalMail.cs
internal class RoyalMail
{
private readonly X509Certificate2 _certificate;
private readonly Config _config;
public RoyalMail()
{
_config = new Config();
_config.LoadConfig();
// Load The SSL Certificate (Check The File Exists)
var certificatePath = (Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + #"\" + _config.GetCertificateName());
if (!File.Exists(certificatePath))
{
throw new Exception(#"The Royal Mail Certificate Is Missing From The Plugins Directory. Please Place The File " + _config.GetCertificateName() + " In The Same Directory As The Plugin DLL File & Relaunch FileMaker.\n\n" + certificatePath);
}
_certificate = new X509Certificate2(certificatePath, _config.GetCertificatePassword());
// Check It's In The Certificate
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
if (!store.Certificates.Contains(_certificate))
{
store.Add(_certificate);
MessageBox.Show("Certificate Was Installed Into Computer Trust Store");
}
store.Close();
}
/*
*
* SOAP Service & Methods
*
*/
private shippingAPIPortTypeClient GetProxy()
{
var myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport)
{
MaxReceivedMessageSize = 2147483647
};
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var uri = new Uri(_config.GetEndpointUrl());
var endpointIdentity = EndpointIdentity.CreateDnsIdentity("api.royalmail.com");
var shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(uri, endpointIdentity, new AddressHeaderCollection()));
if (shippingClient.ClientCredentials != null)
shippingClient.ClientCredentials.ClientCertificate.Certificate = _certificate;
foreach (var od in shippingClient.Endpoint.Contract.Operations)
{
od.Behaviors.Add(new RoyalMailIEndpointBehavior());
}
return shippingClient;
}
private SecurityHeaderType GetSecurityHeaderType()
{
var securityHeader = new SecurityHeaderType();
var creationDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
var nonce = (new Random().Next(0, int.MaxValue)).ToString();
var hashedPassword = GetSha1(_config.GetPassword());
var concatednatedDigestInput = string.Concat(nonce, creationDate, Encoding.Default.GetString(hashedPassword));
var digest = GetSha1(concatednatedDigestInput);
var passwordDigest = Convert.ToBase64String(digest);
var encodedNonce = Convert.ToBase64String(Encoding.Default.GetBytes(nonce));
var doc = new XmlDocument();
using (var writer = doc.CreateNavigator().AppendChild())
{
writer.WriteStartDocument();
writer.WriteStartElement("wsse", "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteStartElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteElementString("wsse", "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", _config.GetUsername());
writer.WriteElementString("wsse", "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", passwordDigest);
writer.WriteElementString("wsse", "Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", encodedNonce);
writer.WriteElementString("wsse", "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", creationDate);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
if (doc.DocumentElement != null)
{
doc.DocumentElement.RemoveAllAttributes();
var headers = doc.DocumentElement.ChildNodes.Cast<XmlElement>().ToArray();
securityHeader.Any = headers;
}
return securityHeader;
}
private integrationHeader GetIntegrationHeader()
{
var header = new integrationHeader();
var created = DateTime.Now;
var createdAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
header.dateTime = created;
header.version = int.Parse(_config.GetVersion());
header.dateTimeSpecified = true;
header.versionSpecified = true;
var idStructure = new identificationStructure {applicationId = _config.GetApplicationId()};
var nonce = new Random().Next(0, int.MaxValue).ToString();
idStructure.transactionId = CalculateMd5Hash(nonce + createdAt);
header.identification = idStructure;
return header;
}
private static byte[] GetSha1(string input)
{
return SHA1Managed.Create().ComputeHash(Encoding.Default.GetBytes(input));
}
public string CalculateMd5Hash(string input)
{
// step 1, calculate MD5 hash from input
var md5 = MD5.Create();
var inputBytes = Encoding.ASCII.GetBytes(input);
var hash = md5.ComputeHash(inputBytes);
// step 2, convert byte array to hex string
var sb = new StringBuilder();
foreach (var t in hash)
{
sb.Append(t.ToString("X2"));
}
return sb.ToString();
}
/*
* Check Response Footer For Errors & Warnings From Service
* If Error Return True So We Can Inform File maker Of Error
* Ignore Warnings For Now
*
*/
private static void CheckErrorsAndWarnings(integrationFooter integrationFooter)
{
if (integrationFooter != null)
{
if (integrationFooter.errors != null && integrationFooter.errors.Length > 0)
{
var errors = integrationFooter.errors;
foreach (var error in errors)
{
MessageBox.Show("Royal Mail Request Error: " + error.errorDescription + ". " + error.errorResolution, "Royal Mail Request Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
if (errors.Length > 0)
{
return;
}
}
if (integrationFooter.warnings != null && integrationFooter.warnings.Length > 0)
{
var warnings = integrationFooter.warnings;
foreach (var warning in warnings)
{
MessageBox.Show("Royal Mail Request Warning: " + warning.warningDescription + ". " + warning.warningResolution, "Royal Mail Request Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
}
}
}
}
/*
* Show Message Box With SOAP Error If We Receive A Fault Code Back From Service
*
*/
private static void ShowSoapException(FaultException e)
{
var message = e.CreateMessageFault();
var errorDetail = message.GetDetail<XmlElement>();
var errorDetails = errorDetail.ChildNodes;
var fullErrorDetails = "";
for (var i = 0; i < errorDetails.Count; i++)
{
var xmlNode = errorDetails.Item(i);
if (xmlNode != null)
fullErrorDetails += xmlNode.Name + ": " + xmlNode.InnerText + "\n";
}
MessageBox.Show("An Error Occured With Royal Mail Service: " + message.Reason + "\n\n" + fullErrorDetails, "Royal Mail SOAP Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
public createShipmentResponse SendCreateShipmentRequest(CreateShipmentForm shippingForm)
{
var client = GetProxy();
try
{
var request = new createShipmentRequest {integrationHeader = GetIntegrationHeader()};
var shipment = new requestedShipment();
// Shipment Type Code (Delivery or Return)
var shipmentType = new referenceDataType {code = shippingForm.ShippingType};
shipment.shipmentType = shipmentType;
// Service Type Code (1:24H 1st Class, 2: 48H 2nd Class, D: Special Delivery Guaranteed, H: HM Forces (BFPO), I: International, R: Tracked Returns, T: Tracked Domestic)
var serviceType = new referenceDataType {code = shippingForm.ServiceType};
shipment.serviceType = serviceType;
// Service Offering (See Royal Mail Service Offering Type Codes. Too Many To List)
var serviceOfferingTypeContainer = new serviceOfferingType();
var serviceOffering = new referenceDataType {code = shippingForm.ServiceOffering};
serviceOfferingTypeContainer.serviceOfferingCode = serviceOffering;
shipment.serviceOffering = serviceOfferingTypeContainer;
// Service Format Code
var serviceFormatTypeContainer = new serviceFormatType();
var serviceFormat = new referenceDataType {code = shippingForm.ServiceFormat};
serviceFormatTypeContainer.serviceFormatCode = serviceFormat;
shipment.serviceFormat = serviceFormatTypeContainer;
// Shipping Date
shipment.shippingDate = shippingForm.ShippingDate;
shipment.shippingDateSpecified = true;
shipment.signature = true;
shipment.signatureSpecified = true;
// Sender Reference Number (e.g. Invoice Number or RA Number)
shipment.senderReference = shippingForm.InvoiceNumber;
/*
* Service Enhancements
*/
var serviceEnhancements = new List<serviceEnhancementType>();
shipment.serviceEnhancements = serviceEnhancements.ToArray();
/*
* Recipient Contact Details
*/
var recipientContact = new contact();
recipientContact.complementaryName = shippingForm.Company;
recipientContact.name = shippingForm.Name;
if(!shippingForm.EmailAddress.Equals("")) {
var email = new digitalAddress {electronicAddress = shippingForm.EmailAddress};
recipientContact.electronicAddress = email;
}
if(!shippingForm.MobileNumber.Equals("")) {
var tel = new telephoneNumber();
var phoneRegex = new Regex(#"[^\d]");
tel.telephoneNumber1 = phoneRegex.Replace(shippingForm.MobileNumber, "");
tel.countryCode = "00" + shippingForm.CountryDiallingCode;
recipientContact.telephoneNumber = tel;
}
shipment.recipientContact = recipientContact;
/*
* Recipient Address
*
*/
var recipientAddress = new address
{
addressLine1 = shippingForm.AddressLine1,
addressLine2 = shippingForm.AddressLine2,
addressLine3 = shippingForm.AddressLine3,
addressLine4 = shippingForm.County,
postTown = shippingForm.Town
};
var country = new countryType();
var countryCode = new referenceDataType { code = shippingForm.CountryCode };
country.countryCode = countryCode;
recipientAddress.country = country;
recipientAddress.postcode = shippingForm.PostCode;
recipientAddress.stateOrProvince = new stateOrProvinceType {stateOrProvinceCode = new referenceDataType()};
shipment.recipientAddress = recipientAddress;
// Shipment Items
var items = new List<item> ();
foreach(var i in shippingForm.Items) {
var item = new item
{
numberOfItems = i.Products.Count.ToString(),
weight = new dimension
{
value = i.Weight*1000,
unitOfMeasure = new unitOfMeasureType {unitOfMeasureCode = new referenceDataType {code = "g"}}
}
};
items.Add(item);
}
if (shippingForm.ServiceType.Contains("international"))
{
var internationalInfo = new internationalInfo
{
shipperExporterVatNo = _config.GetVatNumber(),
documentsOnly = false,
shipmentDescription = "Invoice Number: " + shippingForm.InvoiceNumber,
invoiceDate = DateTime.Now,
termsOfDelivery = "EXW",
invoiceDateSpecified = true,
purchaseOrderRef = shippingForm.InvoiceNumber
};
var parcels = new List<parcel>();
foreach (var i in shippingForm.Items)
{
var parcel = new parcel
{
weight = new dimension
{
value = i.Weight*1000,
unitOfMeasure = new unitOfMeasureType
{
unitOfMeasureCode = new referenceDataType {code = "g"}
}
},
invoiceNumber = shippingForm.InvoiceNumber,
purposeOfShipment = new referenceDataType {code = "31"}
};
var contents = new List<contentDetail>();
foreach (var product in i.Products)
{
var contentDetail = new contentDetail
{
articleReference = product.Sku,
countryOfManufacture = new countryType
{
countryCode = new referenceDataType
{
code = product.CountryOfManufacture
}
},
currencyCode = new referenceDataType {code = product.CurrencyCode},
description = product.Name,
unitQuantity = product.Qty.ToString(),
unitValue = product.Price,
unitWeight = new dimension
{
value = Convert.ToSingle(product.Weight*1000),
unitOfMeasure = new unitOfMeasureType
{
unitOfMeasureCode = new referenceDataType {code = "g"}
}
}
};
contents.Add(contentDetail);
}
//Parcel.contentDetails = Contents.ToArray();
parcels.Add(parcel);
}
internationalInfo.parcels = parcels.ToArray();
shipment.internationalInfo = internationalInfo;
}
else
{
shipment.items = items.ToArray();
}
request.requestedShipment = shipment;
var response = client.createShipment(GetSecurityHeaderType(), request);
// Show Errors And Warnings
CheckErrorsAndWarnings(response.integrationFooter);
return response;
}
catch (TimeoutException e)
{
client.Abort();
MessageBox.Show("Request Timed Out: " + e.Message, "Request Timeout", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
catch (FaultException e)
{
client.Abort();
ShowSoapException(e);
}
catch (CommunicationException e)
{
client.Abort();
MessageBox.Show("A communication error has occurred: " + e.Message + " - " + e.StackTrace, "Communication Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
catch (Exception e)
{
client.Abort();
MessageBox.Show(e.Message, "Royal Mail Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
return null;
}
}
RoyalmailMessage.cs
class RoyalMailMessage : Message
{
public Message _message;
public RoyalMailMessage(Message message)
{
_message = message;
}
public override MessageHeaders Headers
{
get
{
return _message.Headers;
}
}
public override MessageProperties Properties
{
get
{
return _message.Properties;
}
}
public override MessageVersion Version
{
get
{
return _message.Version;
}
}
protected override void OnWriteStartBody(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
_message.WriteBodyContents(writer);
}
protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
{
writer.WriteStartElement("soapenv", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
writer.WriteAttributeString("xmlns", "v2", null, "http://www.royalmailgroup.com/api/ship/V2");
writer.WriteAttributeString("xmlns", "v1", null, "http://www.royalmailgroup.com/integration/core/V1");
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
}
}
RoyalMailMessageFormatter.cs
public class RoyalMailMessageFormatter : IClientMessageFormatter
{
private readonly IClientMessageFormatter _formatter;
public RoyalMailMessageFormatter(IClientMessageFormatter formatter)
{
_formatter = formatter;
}
public object DeserializeReply(Message message, object[] parameters)
{
return _formatter.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
var message = _formatter.SerializeRequest(messageVersion, parameters);
return new RoyalMailMessage(message);
}
}
RoyalMailIEndpointBehavior.cs
internal class RoyalMailIEndpointBehavior : IOperationBehavior
{
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
proxy.Formatter = new RoyalMailMessageFormatter(proxy.Formatter);
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
The error you are getting is basically because of the certificate.
Having said that, I think you should use the v2 of the API as although it is still awful, there are examples out there and you don't need to use a cert.
Rick Strahl has successfully changed the namespaces in the v2 version, see here https://weblog.west-wind.com/posts/2016/Apr/02/Custom-Message-Formatting-in-WCF-to-add-all-Namespaces-to-the-SOAP-Envelope .
There is one new Royal Mail Shipping API 2 available , after I've lost many hours try development the integration with Royal Mail I finally found a way. I'm sharing my project in the git.
https://github.com/americoa/RoyalMailShippingAPIV2
I'm pretty new to the BBG API and its lack of documentation. I've been trying to figure out how to get the DV01 or PV01 of a bond, but I always get an error on the fields I try. Am I missing something? Sample code below that I modified from blapi C# samples.
private void run(string[] args)
{
string serverHost = "localhost";
int serverPort = 8194;
SessionOptions sessionOptions = new SessionOptions();
sessionOptions.ServerHost = serverHost;
sessionOptions.ServerPort = serverPort;
System.Console.WriteLine("Connecting to " + serverHost + ":" + serverPort);
Session session = new Session(sessionOptions);
bool sessionStarted = session.Start();
if (!sessionStarted)
{
System.Console.WriteLine("Failed to start session.");
return;
}
if (!session.OpenService("//blp/refdata"))
{
System.Console.Error.WriteLine("Failed to open //blp/refdata");
return;
}
Service refDataService = session.GetService("//blp/refdata");
Request request = refDataService.CreateRequest("ReferenceDataRequest");
Element securities = request.GetElement("securities");
//IBM Bond BBG004J4QNM9
securities.AppendValue("BBG004J4QNM9");
Element fields = request.GetElement("fields");
fields.AppendValue("DS002");
fields.AppendValue("MARKET_SECTOR_DES");
fields.AppendValue("SECURITY_TYP2");
fields.AppendValue("SECURITY_TYP");
fields.AppendValue("ID_EXCH_SYMBOL");
fields.AppendValue("PX_LAST");
//None of these are returned
fields.AppendValue("PV01_BID_CURRENCY_1");
fields.AppendValue("PV01_MID_CURRENCY_1");
fields.AppendValue("DV01");
fields.AppendValue("HEDGE_RATIO_10Y_TSY");
Console.WriteLine("Sending Request: " + request);
session.SendRequest(request, null);
while (true)
{
Event eventObj = session.NextEvent();
foreach (Message msg in eventObj.GetMessages())
{
Console.WriteLine(msg.AsElement);
}
if (eventObj.Type == Event.EventType.RESPONSE)
{
break;
}
}
}
I have seen references on how to cast to a proxy such as:
((IContextChannel)client.InnerChannel).OperationTimeout = new TimeSpan(0,0,240);
to set an operationtimeout but I am using client = channelFactory.CreateChannel();
How do I cast the channel to IContextChannel? I hope this makes sense. I don't have a complete grasp on channels in WCF.
Thanks
Alexei,
Not sure how to implement your suggestion.
In this code how would I set an operationtimeout?
try
{
Binding multipleTokensBinding = MultiAuthenticationFactorBinding.CreateMultiFactorAuthenticationBinding();
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
EndpointAddress endpointaddress = new EndpointAddress(new Uri("https://justsomeservice"), EndpointIdentity.CreateDnsIdentity("someone.com"));
ChannelFactory<TransActionSvc.TransactionPortType> channelFactory = null;
TransActionSvc.TransactionPortType client = null;
channelFactory = new ChannelFactory<TransActionSvc.TransactionPortType>(multipleTokensBinding, endpointaddress);
BindingElementCollection elements = channelFactory.Endpoint.Binding.CreateBindingElements();
elements.Find<SecurityBindingElement>().IncludeTimestamp = true;
channelFactory.Endpoint.Binding = new CustomBinding(elements);
channelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.Root, X509FindType.FindBySerialNumber, "xxx");
channelFactory.Credentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.Root, X509FindType.FindBySerialNumber, "xxx");
channelFactory.Credentials.UserName.UserName = Properties.Settings.Default.UserName;
channelFactory.Credentials.UserName.Password = Properties.Settings.Default.Password;
TransActionSvc.fetchTranDataAsAttachmentRequest req = new WF_Prod_Svc.TransActionSvc.fetchTranDataAsAttachmentRequest();
TransActionSvc.fetchTranDataAsAttachmentResponse res = new WF_Prod_Svc.TransActionSvc.fetchTranDataAsAttachmentResponse();
TransActionSvc.FetchTranDataAsAttachmentRq_Type reqtype = new WF_Prod_Svc.TransActionSvc.FetchTranDataAsAttachmentRq_Type();
TransActionSvc.FetchTranDataAsAttachmentRs_Type restype = new WF_Prod_Svc.TransActionSvc.FetchTranDataAsAttachmentRs_Type();
TransActionSvc.EndpointReferenceType endpntref = new WF_Prod_Svc.TransActionSvc.EndpointReferenceType();
XmlAttribute actionAttrib1 = doc.CreateAttribute("soapenv", "mustUnderstand", "http://schemas.xmlsoap.org/soap/envelope/");
actionAttrib1.Value = "0";
XmlAttribute actionAttrib2 = doc.CreateAttribute("xmlns");
actionAttrib2.Value = "http://schemas.xmlsoap.org/ws/2003/03/addressing";
XmlAttribute[] objAcctionAtrb = new XmlAttribute[2];
objAcctionAtrb.SetValue(actionAttrib1, 0);
objAcctionAtrb.SetValue(actionAttrib2, 1);
TransActionSvc.AttributedURI action = new WF_Prod_Svc.TransActionSvc.AttributedURI();
action.AnyAttr = objAcctionAtrb;
action.Value = "Transaction";
TransActionSvc.AttributedURI messageid = new WF_Prod_Svc.TransActionSvc.AttributedURI();
messageid.AnyAttr = objAcctionAtrb;
messageid.Value = System.Guid.NewGuid().ToString();
TransActionSvc.AttributedURI to = new WF_Prod_Svc.TransActionSvc.AttributedURI();
to.AnyAttr = objAcctionAtrb;
to.Value = "XGI";
TransActionSvc.EndpointReferenceType endpointreference = new WF_Prod_Svc.TransActionSvc.EndpointReferenceType();
TransActionSvc.ReferencePropertiesType referenceproperties = new WF_Prod_Svc.TransActionSvc.ReferencePropertiesType();
if (Svc_Division.Parsed) { reqtype.division = Svc_Division.StringValue; }
try{reqtype.startDate = Convert.ToDateTime(Svc_StartDate.StringValue);}
catch (FormatException ex){LogMessageToFile("Invalid Start date. " + ex.Message);}
try{reqtype.endDate = Convert.ToDateTime(Svc_EndDate.StringValue);}
catch (FormatException ex){LogMessageToFile("Invalid End date. " + ex.Message);}
if (Svc_DateType.StringValue == "T")
{
reqtype.transactionDateType = TransActionSvc.TransactionDateType_Enum.TransactionDate;
}
else
{
reqtype.transactionDateType = TransActionSvc.TransactionDateType_Enum.PostingDate;
}
switch (Svc_TransType.StringValue)
{
case "OOP":
reqtype.transactionType = TransActionSvc.TransactionType_Enum.OOP;
break;
case "CHARGES":
reqtype.transactionType = TransActionSvc.TransactionType_Enum.CHARGES;
break;
default:
reqtype.transactionType = TransActionSvc.TransactionType_Enum.ALL;
break;
}
System.Xml.XmlElement companyid = doc.CreateElement("companyId");
companyid.InnerText = Properties.Settings.Default.CompanyID;
System.Xml.XmlElement[] objectarray = new System.Xml.XmlElement[1];
objectarray.SetValue(companyid, 0);
referenceproperties.Any = objectarray;
endpointreference.ReferenceProperties = referenceproperties;
req.Action = action;
req.MessageID = messageid;
req.To = to;
req.ReplyTo = endpointreference;
req.fetchTranDataAsAttachment = reqtype;
try
{
client = channelFactory.CreateChannel();
//THIS DOES NOT WORK
client.OperationTimeout = new TimeSpan(0,10,0);
res = client.fetchTranDataAsAttachment(req);
if (res.fetchTranDataAsAttachmentResponse1.WFFaultList != null)
{
LogLine = string.Format("FaultCode({0});FaultType({1});FaultReason({2});Severity({3})",
res.fetchTranDataAsAttachmentResponse1.WFFaultList[0].faultCode,
res.fetchTranDataAsAttachmentResponse1.WFFaultList[0].faultType,
res.fetchTranDataAsAttachmentResponse1.WFFaultList[0].faultReasonText,
res.fetchTranDataAsAttachmentResponse1.WFFaultList[0].severity);
}
if (res.fetchTranDataAsAttachmentResponse1.attachment != null)
{
string attachFileName = res.fetchTranDataAsAttachmentResponse1.attachment.fileName;
byte[] filebytes = res.fetchTranDataAsAttachmentResponse1.attachment.binaryData.Value;
FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "..\\..\\" + res.fetchTranDataAsAttachmentResponse1.attachment.fileName, FileMode.CreateNew, FileAccess.Write, FileShare.None);
fs.Write(filebytes, 0, filebytes.Length);
fs.Close();
DateTime EndReceive = DateTime.Now;
TimeSpan elapsed = EndReceive.Subtract(BeginReceive);
LogLine = string.Format("Arguments: [{0}]; FileName: {1}; FileSize: {2} bytes; ElapsedTime: {3} seconds", arguments.Trim(), attachFileName, filebytes.Length.ToString(), elapsed.TotalSeconds.ToString());
LogMessageToFile(LogLine);
}
}
catch (CommunicationException ex1)
{
Abort((IChannel)client, channelFactory);
FaultException fe = null;
Exception tmp = ex1;
while (tmp != null)
{
fe = tmp as FaultException;
if (fe != null)
{
break;
}
tmp = tmp.InnerException;
}
if (fe != null)
{
string errmsg = string.Format("The server sent back a fault: {0}", fe.CreateMessageFault().Reason.GetMatchingTranslation().Text);
LogMessageToFile(errmsg);
}
else
{
string errmsg = string.Format("The request failed with exception: {0}", ex1.Message.ToString());
LogMessageToFile(errmsg);
}
}
catch (TimeoutException)
{
Abort((IChannel)client, channelFactory);
string errmsg = string.Format("The request timed out ");
DateTime EndReceive = DateTime.Now;
TimeSpan elapsed = EndReceive.Subtract(BeginReceive);
LogLine = string.Format("Arguments: [{0}]; Exception: {1}; ElapsedTime: {2} seconds", arguments.Trim(), errmsg, elapsed.TotalSeconds.ToString());
LogMessageToFile(LogLine);
}
catch (Exception ex)
{
Abort((IChannel)client, channelFactory);
string errmsg = string.Format("The request failed with unexpected exception: {0}", ex.Message.ToString());
LogMessageToFile(errmsg);
}
finally
{
((IChannel)client).Close();
channelFactory.Close();
}
}
You will not get any usefuls (non-null/non-exception) result by casting channel factory to channel interface since there is no point for factory to implement any of channel interfaces.
You can cast channel to some other channel interface and likely get useful result if you know what type of channel is used like in the code your refer to.
EDIT: I think ((IContextChannel)channel).OperationTimeout = new TimeSpan(0,10,0); should work.
Note: title of your post does not match your code...