wcf - Royal Mail Shipping API C# -
i'm trying integrate royal mail soap api .net code. have followed advice here consume wcf royal mail api in c# console application , here c# wcf namespaces move header & use ns prefix.
i have created custom iclientmessageformatter able attach namespaces beginning of soap envelope, still can't seem work. keep receiving following error. not establish trust relationship ssl/tls secure channel authority 'api.royalmail.com', , inner exception is: remote certificate invalid according validation procedure.
i using visual studio 13 , .net version 3.5, i've tried numerous other versions no further progress. when debug can see normal message been passed royalmailmessage when runs onwritestartenvelope can't see changes _message object. i've created trace see soap request been sent.
i have sent xml request royal mail support validate reason failing due namespaces not been declared in envelope , missing prefixes.
royalmail.cs
internal class royalmail { private readonly x509certificate2 _certificate; private readonly config _config; public royalmail() { _config = new config(); _config.loadconfig(); // load ssl certificate (check file exists) var certificatepath = (path.getdirectoryname(system.reflection.assembly.getexecutingassembly().location) + @"\" + _config.getcertificatename()); if (!file.exists(certificatepath)) { throw new exception(@"the royal mail certificate missing plugins directory. please place file " + _config.getcertificatename() + " in same directory plugin dll file & relaunch filemaker.\n\n" + certificatepath); } _certificate = new x509certificate2(certificatepath, _config.getcertificatepassword()); // check it's in certificate var store = new x509store(storename.my, storelocation.currentuser); store.open(openflags.readwrite); if (!store.certificates.contains(_certificate)) { store.add(_certificate); messagebox.show("certificate installed 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 input var md5 = md5.create(); var inputbytes = encoding.ascii.getbytes(input); var hash = md5.computehash(inputbytes); // step 2, convert byte array hex string var sb = new stringbuilder(); foreach (var t in hash) { sb.append(t.tostring("x2")); } return sb.tostring(); } /* * check response footer errors & warnings service * if error return true can inform file maker of error * ignore warnings * */ 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 soap error if receive fault code service * */ private static void showsoapexception(faultexception e) { var message = e.createmessagefault(); var errordetail = message.getdetail<xmlelement>(); var errordetails = errordetail.childnodes; var fullerrordetails = ""; (var = 0; < errordetails.count; i++) { var xmlnode = errordetails.item(i); if (xmlnode != null) fullerrordetails += xmlnode.name + ": " + xmlnode.innertext + "\n"; } messagebox.show("an error occured 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. many 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 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 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 , 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 { { return _message.headers; } } public override messageproperties properties { { return _message.properties; } } public override messageversion version { { 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 getting because of certificate.
having said that, think should use v2 of api although still awful, there examples out there , don't need use cert.
rick strahl has changed namespaces in 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 .
Comments
Post a Comment