Search code examples
c#web-serviceswcfquickbooksconnector

No matching contract between WCF Service and QuickBooks Web Connector


I am trying to write a small SOAP server, which connects to the QuickBooks Web Connector, but I have some trouble to find the correct contracts. I always get following error:

Web Connector

Method x cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

I created an empty ASP .NET Web Application and added a WCF Service. You will find here a snippet of the authenticate method:

WCF Service interface

[ServiceContract]
public interface IQuickBooks
{
    [OperationContract]
    AuthenticateResponse authenticate(Authenticate authenticateSoapIn);
}

WCF Service implementation

public class QuickBooks : IQuickBooks
{
    public AuthenticateResponse authenticate(Authenticate authenticateSoapIn)
    {
        return new AuthenticateResponse
        {
            AuthenticateResult = new[] { "1", "none" }
        };
    }
}

Request

[DataContract(Name = "authenticate")]
public class Authenticate
{
    [DataMember(Name = "strUserName", IsRequired = true)]
    public string Username { get; set; }

    [DataMember(Name = "strPassword", IsRequired = true)]
    public string Password { get; set; }
}

Response

[DataContract(Name = "authenticateResponse")]
public class AuthenticateResponse
{
    [DataMember(Name = "authenticateResult", IsRequired = true)]
    public string[] AuthenticateResult { get; set; }
}

Here you can find the WSDL from QuickBooks and my WSDL output. Notice that I only implemented the authenticate method for testing. I guess the mismatching wsdl:types cause the error. In the original WSDL from QuickBooks the authenticate type has two primitive types for username and password.

How could I implement a WCF Service with QuickBooks Web Connector? What did I wrong?

Additional information

StackTrace

The message with Action 'http://developer.intuit.com/authenticate' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
More info:
StackTrace =    at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at QBWebConnector.localhost.WCWebServiceDoc.authenticate(String strUserName, String strPassword)
   at QBWebConnector.localhost.WCWebService.authenticate(String strUserName, String strPassword)
   at QBWebConnector.SOAPWebService.authenticate(String UserName, String Password)
   at QBWebConnector.WebService.do_authenticate(String& ticket, String& companyFileName)

Solution

  • This answer describes how to connect a WCF Service with the QuickBooks Web Connecter (e. g. authenticate method). I am not totally sure if it is the best implementation, but it works and I would like to help other people with similar problems. Enchantments and additional suggestions are always welcome.

    1. Create an empty ASP .NET Web Application
    2. Add a WCF Service
    3. Define the service contract
    4. Implement the service behavior
    5. Define the necessary data types

    Create the service contract

    [ServiceContract(Namespace = QuickBooks.URL, Name = "QuickBooks")]
    public interface IQuickBooks
    {
        [OperationContract(Action = QuickBooks.URL + "authenticate")]
        AuthenticateResponse authenticate(Authenticate authenticateSoapIn);
    }
    

    Create the service behavior

    [ServiceBehavior(Namespace = QuickBooks.URL)]
    public class QuickBooks : IQuickBooks
    {
        public const string URL = "http://developer.intuit.com/";
    
        public AuthenticateResponse authenticate(Authenticate authenticateSoapIn)
        {
            // Check if authenticateSoapIn is valid
    
            var authenticateResponse = new AuthenticateResponse();
            authenticateResponse.AuthenticateResult.Add(System.Guid.NewGuid().ToString());
            authenticateResponse.AuthenticateResult.Add(string.Empty);
    
            return authenticateResponse;
        }
    }
    

    Implement the request and response types

    Request

    [DataContract(Name = "authenticate")]
    [MessageContract(WrapperName = "authenticate", IsWrapped = true)]
    public class Authenticate
    {
        [DataMember(Name = "strUserName", IsRequired = true)]
        [MessageBodyMember(Name = "strUserName", Order = 1)]
        public string Username { get; set; }
    
        [DataMember(Name = "strPassword", IsRequired = true)]
        [MessageBodyMember(Name = "strPassword", Order = 2)]
        public string Password { get; set; }
    
        public Authenticate()
        {
        }
    
        public Authenticate(string username, string password)
        {
            this.Username = username;
            this.Password = password;
        }
    }
    

    Response

    [DataContract(Name = "authenticateResponse")]
    [MessageContract(WrapperName = "authenticateResponse", IsWrapped = true)]
    public class AuthenticateResponse
    {
        [DataMember(Name = "authenticateResult", IsRequired = true)]
        [MessageBodyMember(Name = "authenticateResult", Order = 1)]
        public ArrayOfString AuthenticateResult { get; set; }
    
        public AuthenticateResponse()
        {
            this.AuthenticateResult = new ArrayOfString();
        }
    
        public AuthenticateResponse(ArrayOfString authenticateResult)
        {
            this.AuthenticateResult = authenticateResult;
        }
    }
    

    ArrayOfString used in authenticateResponse

    [CollectionDataContractAttribute(Name = "ArrayOfString", Namespace = QuickBooks.URL, ItemName = "string")]
    public class ArrayOfString : List<string>
    {
    }
    

    This scheme complies to the SOAP contract and allows the data exchange.