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>
The following code is charging the card, however it is not creating the profile....any tips? I'm assuming I'm missing something, or using the wrong Type...
var opaqueData = new opaqueDataType { dataDescriptor = "COMMON.ACCEPT.INAPP.PAYMENT", dataValue = paymentNonce };
//standard api call to retrieve response
var paymentType = new paymentType { Item = opaqueData };
var transactionRequest = new transactionRequestType
{
transactionType = transactionTypeEnum.authCaptureTransaction.ToString(), // authorize and capture transaction
amount = paymentAmount,
payment = paymentType,
customer = new customerDataType()
{
type = customerTypeEnum.individual,
id = userID.ToString()
},
profile = new customerProfilePaymentType()
{
createProfile = true
}
};
var request = new createTransactionRequest { transactionRequest = transactionRequest };
// instantiate the contoller that will call the service
var controller = new createTransactionController(request);
const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
controller.Execute();
// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
UPDATE:
Since apparently OpaqueData is not allowed, I changed it to make the profile manually. I am getting the following Error: "Error: I00001 Successful."
// Add Payment method to Customer.
customerPaymentProfileType opaquePaymentProfile = new customerPaymentProfileType();
opaquePaymentProfile.payment = paymentType;
opaquePaymentProfile.customerType = customerTypeEnum.individual;
var request2 = new createCustomerPaymentProfileRequest
{
paymentProfile = opaquePaymentProfile,
validationMode = validationModeEnum.none,
customerProfileId = userID.ToString()
};
var controller2 = new createCustomerPaymentProfileController(request2);
controller2.Execute();
//Send Request to EndPoint
createCustomerPaymentProfileResponse response2 = controller2.GetApiResponse();
if (response2 != null && response2.messages.resultCode == messageTypeEnum.Ok)
{
if (response2 != null && response2.messages.message != null)
{
//Console.WriteLine("Success, createCustomerPaymentProfileID : " + response.customerPaymentProfileId);
}
}
else
{
Utility.AppendTextToFile("Error: " + response.messages.message[0].code + " " + response.messages.message[0].text, Server.MapPath("/pub/auth.txt"));
}
Update #2
Very confused as auth.net documentation says this code means success...so why don't I see the CIM payment method created??? RESPONSE CODE DOCS
Update #3
So I was printing out the main response message instead of the CIM request message, duh. The actual error was: "E00114 Invalid OTS Token."
Based on the the documentation, that error is usually from a used Key, so I am now generating 2 keys (One to process and One to store via CIM) but am now getting this error: "E00040 The record cannot be found."....Any ideas?
So the answer to this question is:
You can not auto create a payment profile using opaque card data, so the answer is to make it manually once you have a successful charge.
You can not use the same opaque card data to charge and store, as they are one time use, so for my web method I ended up passing 2 opaque data keys.
You have to make different calls for setting up a brand new customer and an existing customer just adding a new card. I have pasted an excerpt of my end solution below:
ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment = (System.Configuration.ConfigurationManager.AppSettings["Authorize-Live"].ToUpper() == "TRUE" ? AuthorizeNet.Environment.PRODUCTION : AuthorizeNet.Environment.SANDBOX);
// define the merchant information (authentication / transaction id)
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication = new merchantAuthenticationType()
{
name = (System.Configuration.ConfigurationManager.AppSettings["Authorize-Live"].ToUpper() == "TRUE" ? System.Configuration.ConfigurationManager.AppSettings["Authorize-LoginID"] : System.Configuration.ConfigurationManager.AppSettings["Authorize-LoginID-SandBox"]),
ItemElementName = ItemChoiceType.transactionKey,
Item = (System.Configuration.ConfigurationManager.AppSettings["Authorize-Live"].ToUpper() == "TRUE" ? System.Configuration.ConfigurationManager.AppSettings["Authorize-TransactionKey"] : System.Configuration.ConfigurationManager.AppSettings["Authorize-TransactionKey-SandBox"])
};
if (paymentNonce.Trim() != "")
{
//set up data based on transaction
var opaqueData = new opaqueDataType { dataDescriptor = "COMMON.ACCEPT.INAPP.PAYMENT", dataValue = paymentNonce };
//standard api call to retrieve response
var paymentType = new paymentType { Item = opaqueData };
var transactionRequest = new transactionRequestType
{
transactionType = transactionTypeEnum.authCaptureTransaction.ToString(), // authorize and capture transaction
amount = paymentAmount,
payment = paymentType,
customer = new customerDataType()
{
type = customerTypeEnum.individual,
id = "YOUR_DB_USERID"
},
profile = new customerProfilePaymentType()
{
createProfile = false
}
};
var request = new createTransactionRequest { transactionRequest = transactionRequest };
// instantiate the contoller that will call the service
var controller = new createTransactionController(request);
const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
controller.Execute();
// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
//validate
if (response != null)
{
if (response.messages.resultCode == messageTypeEnum.Ok)
{
if (response.transactionResponse.messages != null)
{
responseData.Success = true;
transactionID = response.transactionResponse.transId;
string merchID = "STORED AUTHORIZE.NET CUSTOMERID, return blank string if none!";
var opaqueData2 = new opaqueDataType { dataDescriptor = "COMMON.ACCEPT.INAPP.PAYMENT", dataValue = paymentNonce2 };
//standard api call to retrieve response
var paymentType2 = new paymentType { Item = opaqueData2 };
customerPaymentProfileType opaquePaymentProfile = new customerPaymentProfileType();
opaquePaymentProfile.payment = paymentType2;
opaquePaymentProfile.customerType = customerTypeEnum.individual;
if (merchID == "")
{
// CREATE NEW AUTH.NET AIM CUSTOMER
List<customerPaymentProfileType> paymentProfileList = new List<customerPaymentProfileType>();
paymentProfileList.Add(opaquePaymentProfile);
customerProfileType customerProfile = new customerProfileType();
customerProfile.merchantCustomerId = "YOUR_DB_USERID";
customerProfile.paymentProfiles = paymentProfileList.ToArray();
var cimRequest = new createCustomerProfileRequest { profile = customerProfile, validationMode = validationModeEnum.none };
var cimController = new createCustomerProfileController(cimRequest); // instantiate the contoller that will call the service
cimController.Execute();
createCustomerProfileResponse cimResponse = cimController.GetApiResponse();
if (cimResponse != null && cimResponse.messages.resultCode == messageTypeEnum.Ok)
{
if (cimResponse != null && cimResponse.messages.message != null)
{
// STORE cimResponse.customerProfileId IN DATABASE FOR USER
}
}
else
{
for (int i = 0; i < cimResponse.messages.message.Length; i++)
Utility.AppendTextToFile("New Error (" + merchID + ") #" + i.ToString() + ": " + cimResponse.messages.message[i].code + " " + cimResponse.messages.message[i].text, Server.MapPath("/pub/auth.txt"));
}
}
else
{
// ADD PAYMENT PROFILE TO EXISTING AUTH.NET AIM CUSTOMER
var cimRequest = new createCustomerPaymentProfileRequest
{
paymentProfile = opaquePaymentProfile,
validationMode = validationModeEnum.none,
customerProfileId = merchID.Trim()
};
var cimController = new createCustomerPaymentProfileController(cimRequest);
cimController.Execute();
//Send Request to EndPoint
createCustomerPaymentProfileResponse cimResponse = cimController.GetApiResponse();
if (cimResponse != null && cimResponse.messages.resultCode == messageTypeEnum.Ok)
{
if (cimResponse != null && cimResponse.messages.message != null)
{
//Console.WriteLine("Success, createCustomerPaymentProfileID : " + response.customerPaymentProfileId);
}
}
else
{
for (int i = 0; i < cimResponse.messages.message.Length; i++)
Utility.AppendTextToFile("Add Error (" + merchID + ") #" + i.ToString() + ": " + cimResponse.messages.message[i].code + " " + cimResponse.messages.message[i].text, Server.MapPath("/pub/auth.txt"));
}
}
}
else
{
responseData.Message = "Card Declined";
responseData.Success = false;
if (response.transactionResponse.errors != null)
{
responseData.Message = response.transactionResponse.errors[0].errorText;
}
}
}
else
{
responseData.Message = "Failed Transaction";
responseData.Success = false;
if (response.transactionResponse != null && response.transactionResponse.errors != null)
{
responseData.Message = response.transactionResponse.errors[0].errorText;
}
else
{
responseData.Message = response.messages.message[0].text;
}
}
}
else
{
responseData.Message = "Failed Transaction, Try Again!";
responseData.Success = false;
}
}
else
{
// RUN PAYMENT WITH STORED PAYMENT PROFILE ID
customerProfilePaymentType profileToCharge = new customerProfilePaymentType();
profileToCharge.customerProfileId = CustomerID;
profileToCharge.paymentProfile = new paymentProfile { paymentProfileId = PaymentID };
var transactionRequest = new transactionRequestType
{
transactionType = transactionTypeEnum.authCaptureTransaction.ToString(),
amount = paymentAmount,
profile = profileToCharge
};
var request = new createTransactionRequest { transactionRequest = transactionRequest };
// instantiate the collector that will call the service
var controller = new createTransactionController(request);
controller.Execute();
// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
//validate
if (response != null)
{
if (response.messages.resultCode == messageTypeEnum.Ok)
{
if (response.transactionResponse.messages != null)
{
responseData.Success = true;
transactionID = response.transactionResponse.transId;
}
else
{
responseData.Message = "Card Declined";
responseData.Success = false;
if (response.transactionResponse.errors != null)
{
responseData.Message = response.transactionResponse.errors[0].errorText;
}
}
}
else
{
responseData.Message = "Failed Transaction";
responseData.Success = false;
if (response.transactionResponse != null && response.transactionResponse.errors != null)
{
responseData.Message = response.transactionResponse.errors[0].errorText;
}
else
{
responseData.Message = response.messages.message[0].text;
}
}
}
else
{
responseData.Message = "Failed Transaction, Try Again!";
responseData.Success = false;
}
}
I am trying to implement a code to modify entities through WCF Dataservices from the OData client I have generated. I have followed the same principle that was implemented in this link - https://msdn.microsoft.com/en-us/library/dd756368(v=vs.110).aspx
I am getting "CSRF token validation failed" error during the SaveChanges method call. Please see code below:
// Define the URI of the public Northwind OData service.
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Uri cuanUri =
new Uri("https://xxxx.xxxx.com:44300/sap/opu/odata/sap/CUAN_IMPORT_SRV/",
UriKind.Absolute);
// Create a new instance of the typed DataServiceContext.
DateTime stimestamp = new DateTime();
DateTime dob = new DateTime(1992,05,02);
CUAN_IMPORT_SRV_Entities context = new CUAN_IMPORT_SRV_Entities(cuanUri);
context.SendingRequest2 += SendBaseAuthCredsOnTheRequest;
Contact newContact = Contact.CreateContact("C160717055735", "SAP_ODATA_IMPORT", stimestamp);
//Product newProduct = Product.CreateProduct(0, "White Tea - loose", false);
newContact.CompanyId = "BLUEFIN1";
newContact.CompanyIdOrigin = "SAP_ODATA_IMPORT";
newContact.CountryDescription = "Singapore";
newContact.DateOfBirth = dob;
newContact.EMailAddress = "j_prado#yahoo.com";
newContact.EMailOptIn = "Y";
newContact.FirstName = "Jeffrey1";
newContact.FunctionDescription = "Consultant";
newContact.GenderDescription = "Male";
newContact.LastName = "Prado1";
newContact.PhoneNumber = "+6596492714";
newContact.PhoneOptin = "Y";
Company newCompany = Company.CreateCompany("BLUEFIN1", "SAP_ODATA_IMPORT", stimestamp);
newCompany.IndustryDescription = "1007";
newCompany.CompanyName = "BLUEFIN1";
Interaction newInteraction = Interaction.CreateInteraction("");
newInteraction.CommunicationMedium = "WEB";
newInteraction.ContactId = "C160717055735";
newInteraction.ContactIdOrigin = "SAP_ODATA_IMPORT";
newInteraction.InteractionType = "WEBSITE_REGISTRATION";
newInteraction.Timestamp = stimestamp;
ImportHeader newHeader = ImportHeader.CreateImportHeader("");
newHeader.Timestamp = stimestamp;
newHeader.UserName = "ENG";
newHeader.SourceSystemType = "EXT";
newHeader.SourceSystemId = "HYBRIS";
try
{
// Add the new entities to the CUAN entity sets.
context.AddToImportHeaders(newHeader);
context.AddToContacts(newContact);
context.AddToCompanies(newCompany);
context.AddToInteractions(newInteraction);
// Send the insert to the data service.
DataServiceResponse response = context.SaveChanges();
// Enumerate the returned responses.
foreach (ChangeOperationResponse change in response)
{
// Get the descriptor for the entity.
EntityDescriptor descriptor = change.Descriptor as EntityDescriptor;
if (descriptor != null)
{
Contact addedContact = descriptor.Entity as Contact;
if (addedContact != null)
{
Console.WriteLine("New contact added with ID {0}.",
addedContact.CompanyId);
}
}
}
}
catch (DataServiceRequestException ex)
{
throw new ApplicationException(
"An error occurred when saving changes.", ex);
}
}
private static void SendBaseAuthCredsOnTheRequest(object sender,
SendingRequest2EventArgs e)
{
var authHeaderValue = Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}"
, "XXXXX", "XXXXX")));
e.RequestMessage.SetHeader("Authorization", "Basic " + authHeaderValue); //this is where you pass the creds.
}
The code above fails when invoking: DataServiceResponse response = context.SaveChanges();
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've using nekodrive library.
And i need to add functionality to change last modified date of file. Found method SetAttr. NFSv3.cs And apply similar to SetFileSize function to set modified date:
public void SetLastModifiedDate(string FileFullName, DateTime LastModified)
{
if (_ProtocolV3 == null)
{ throw new NFSConnectionException("NFS Client not connected!"); }
if (_MountProtocolV3 == null)
{ throw new NFSMountConnectionException("NFS Device not connected!"); }
NFSAttributes Attributes = GetItemAttributes(FileFullName);
SetAttributeArguments dpArgSAttr = new SetAttributeArguments();
NFSTimeValue lastModified = new NFSTimeValue();
lastModified.Seconds = (int)(LastModified - new DateTime(1970, 1, 1)).TotalSeconds;
dpArgSAttr.Handle = new NFSHandle(Attributes.Handle, V3.RPC.NFSv3Protocol.NFS_V3);
dpArgSAttr.Attributes = new MakeAttributes();
dpArgSAttr.Attributes.LastAccessedTime = new NFSTimeValue();
dpArgSAttr.Attributes.ModifiedTime = lastModified;
dpArgSAttr.Attributes.SetModifiedTime = TimeHow.SET_TO_CLIENT_TIME;
dpArgSAttr.Attributes.Mode = Attributes.Mode;
dpArgSAttr.Attributes.UserID = -1;
dpArgSAttr.Attributes.GroupID = -1;
dpArgSAttr.Attributes.SetSize = false;
dpArgSAttr.GuardCreateTime = new NFSTimeValue();
dpArgSAttr.GuardCheck = false;
ResultObject<SetAttributeAccessOK, SetAttributeAccessFAIL> pAttrStat =
_ProtocolV3.NFSPROC3_SETATTR(dpArgSAttr);
if (pAttrStat == null || pAttrStat.Status != NFSStats.NFS_OK)
{
if (pAttrStat == null)
{ throw new NFSGeneralException("NFSPROC3_SETATTR: failure"); }
ExceptionHelpers.ThrowException(pAttrStat.Status);
}
}
Unfortunately i get an error: Update synchronization mismatch was detected during a SETATTR operation. Error Code is NFSERR_NOT_SYNC = 10002
Exports file:
/home/nfs *(rw)
Can anyone advice on what could be wrong? may be just NFS configuration?