16

I wish to use this API with a c# application: http://www.affjet.com/2012/11/26/4-4-affjet-api/#more-3099

After adding the wsdl to my projcet i wrote this simple code : (getTransactions gets a object[] @params and returns a string)

Ws_ApiService service = new Ws_ApiService();
string apiKey = "*************";
var response = service.getTransactions(new object[] { apiKey });

i tried few more ways but couldnt get a right response, i tried :

var response = service.getTransactions(new object[] { "apiKey:****"});

and

var response = service.getTransactions(new object[] { "apiKey","******"});

Here is the php code that does the same from their docs :

<?php

$nameSpace = "https://secure.affjet.com/ws/api";

//Creating AffJet client for SOAP
$client = new SoapClient($nameSpace."?wsdl");

$pageNumber = 0;
//Setting up parameters
$params = array();
$params["apiKey"] = "MY_API_KEY";
//Value for parameters (optional)
//$params["networkId"] = array(1,2);
//$params["pageNumber"] = 0;
//$params["pageSize"] = 10;
//Making Request
$response = $client->getNetworks($params);
//XML to SimpleXMLElement Object
$xmlResponse = new SimpleXMLElement($response);
if ($xmlResponse->success == "true"){
    while (isset($xmlResponse->dataList->data)) {
        //Iterate the results
        foreach ($xmlResponse->dataList->data as $data){
            var_dump(xml2array($data));
        }
        //Requesting next page of data
        $pageNumber++;
        $params["pageNumber"] = $pageNumber;
        //Making Request
        $response = $client->getNetworks($params);
        //XML to SimpleXMLElement Object
        $xmlResponse = new SimpleXMLElement($response);
    }
} else {
    //Error somewhere
    echo $xmlResponse->errorMessage;
}

/**
* Transforms the object SimpleXmlElement into an array, easier to handle
*/
function xml2array($xml) {
    $arr = array();
    foreach ($xml as $element) {
        $tag = $element->getName();
        $e = get_object_vars($element);
        if (!empty($e)) {
            $arr[$tag] = $element instanceof SimpleXMLElement ? xml2array($element) : $e;
        } else {
            $arr[$tag] = trim($element);
        }
    }
    return $arr;
}


?>

this was the response for what i tried :

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:ns1="http://secure.affjet.com/ws/api"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
          SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:getTransactionsResponse>
        <return xsi:type="xsd:string">
            &lt;response&gt;&lt;success&gt;false&lt;/success&gt;&lt;errorMessage&gt;
            API Key not provided&lt;/errorMessage&gt;&lt;dataList&gt;
            &lt;/dataList&gt;&lt;/response&gt;
        </return>
        </ns1:getTransactionsResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

you can see :

API Key not provided

And the response should be something like this:

<response>
    <success>true</success>
    <errorMessage></errorMessage>
    <dataList>
        <data>
            <date>2012-11-05 15:02:41</date>//Transaction Date
            <amount>81.67</amount>
            <commission>15.86</commission>
            <status>confirmed</status>//Status, could be: declined, pending, confirmed or paid
            <clickDate></clickDate>
            <ip></ip>
            <custom_id>MyCustomId</custom_id>//Custom Id for the transactions (SID, SubId,clickRef....)
            <unique_id>2548462</unique_id>//Unique Id given from the network to this transaction
            <merchantId>1</merchantId>//Id for the Merchant on AffJet
            <networkId>1</networkId>//Id for the Network on AffJet
        </data>
    </dataList>
</response>

all i need to supply is a parameter named "apiKey" and its value

EDIT :

after contacting their support, they said the request should look like this :

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:ns1="http://secure.affjet.com/ws/api" 
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                   xmlns:ns2="http://xml.apache.org/xml-soap" 
                   xmlns:SOAP- ENC="http://schemas.xmlsoap.org/soap/encoding/" 
            SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:getTransactions>
            <params xsi:type="ns2:Map">
                <item>
                    <key xsi:type="xsd:string">apiKey</key>
                    <value xsi:type="xsd:string">YOURAPIKEY</value>
                </item>
            </params>
        </ns1:getTransactions>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Any ideas ?

Sam R.
  • 16,027
  • 12
  • 69
  • 122
Matan L
  • 997
  • 3
  • 14
  • 35
  • 1
    What happened with what you tried? Returned a string? What was the string? What did you expect it to be? – Scott Adams Apr 25 '13 at 17:27
  • I edited what you asked for :) – Matan L Apr 25 '13 at 17:32
  • Can you see what the method sig is for service.getTransactions()? – Scott Adams Apr 25 '13 at 17:37
  • how can i see it ? in the object browser this is what i can see : public string getTransactions(object[] params) – Matan L Apr 25 '13 at 17:39
  • 2
    When i try to use WCFTestClient to import the wdsl, I get an error adding the service. How did you add the wdsl to your project? Web ref? – Scott Adams Apr 25 '13 at 17:52
  • Add service reference -> advanced -> add web reference ... – Matan L Apr 25 '13 at 20:02
  • 2
    I just tried wsdl.exe https://secure.affjet.com/ws/api?wsdl and got a lot of warnings and errors. Try that out. I think the warning "Warning: This web reference does not conform to WS-I Basic Profile v1.1." may be something to look at further. – Scott Adams Apr 25 '13 at 20:37
  • http://stackoverflow.com/questions/1102020/consuming-php-webservicesoap-wsdl-from-asp-net-c-sharp-app-problems-with-ar Here is a SO post with the exact problem, however they had access to the server code and just gave up and changed the sig. Unfortunately, you cant do that. Sorry, I really wanted to be able to help you with this. API writers out there, make sure you use standard types. – Scott Adams Apr 25 '13 at 20:52
  • Have you tried something like `new object[] { new { apiKey = "" } } ` – Dustin Kingen Apr 27 '13 at 17:37
  • Romoku - trying your way gives the following exception - {"<>f__AnonymousType2`1[System.String] cannot be serialized because it does not have a parameterless constructor."} – Matan L Apr 27 '13 at 20:10
  • You’ll have to cast some attributes like here: . – Stefan Paletta May 03 '13 at 20:35
  • Have you tried using [Fiddler](http://fiddler2.com/get-fiddler) to inspect the request that your code is actually sending? Now that you have the expected request format, you can compare this to your request, which should help you identify the problem. – bcpettifer May 04 '13 at 10:31
  • One solution is to write your SOAP request manually: http://stackoverflow.com/questions/287126/how-to-post-soap-request-from-net – Simon Mourier May 04 '13 at 14:49
  • @Stefan Paletta - i will give this a try i think it might be the way! – Matan L May 05 '13 at 09:10
  • @jaminja - yes i did but could not make the request as it should be ... – Matan L May 05 '13 at 09:11
  • @Simon Mourier - i have already tried that using another soap code that i am using for another wsdl that works but no success here... i can see the request sent as it should be in fiddler but the response gives me back the wsdl xml code... – Matan L May 05 '13 at 09:11
  • @MatanL - Well it certainly *can* work, no magic here, but it's not very easy. Check carefully fiddler, that may be a header problem, not only the payload. – Simon Mourier May 05 '13 at 09:14
  • @Simon Mourier - well that was somthing wrong not about the headers bit about the url format... anyway i managed to do that this way, but there must be a normal way to pass the parameters ... – Matan L May 05 '13 at 12:49
  • @MatanL - not sure it's feasible. SVCUtil.exe complains about this WSDL, not even capable of generating a proxy, and only the old "Add Web Reference" thing seems to work initially (what you have tried) and fails at runtime. See this for similar issue : http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/7a84864a-539f-4e3f-b788-b1bccb24f524/ – Simon Mourier May 05 '13 at 13:54

3 Answers3

3

I dug a little deeper into this fascinating topic and have to say that working with what amounts to an associative array is just a pain in the .NET SOAP implementation. An associative array is represented as an IDictionary (e.g. Hashtable) – but refuses to be serialized (try it!).

The below code is the closest I could get – and for some reason (bug?) it does not work on the .NET framework, but does work on Mono.

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;

public class Test
{
  /*
   *  as per http://stackoverflow.com/a/1072815/2348103
   */
  public class Item
  {
    [XmlElement(Form = XmlSchemaForm.Unqualified)]
    public string key;

    [XmlElement(Form = XmlSchemaForm.Unqualified)]
    public string value;
  }

  [SoapType(TypeName = "Map", Namespace = "http://xml.apache.org/xml-soap")]
  public class Map : List<Item>
  {
  }

  public static void Main()
  {
    Map map = new Map();
    map.Add( new Item { key="foo", value="bar" } );
    map.Add( new Item { key="quux", value="barf" } );

    XmlTypeMapping mapping = 
         (new SoapReflectionImporter()).ImportTypeMapping( map.GetType() );
    XmlSerializer serializer = new XmlSerializer( mapping );
    XmlTextWriter writer = new XmlTextWriter( System.Console.Out );
    writer.Formatting = Formatting.Indented;
    writer.WriteStartElement( "root" );
    serializer.Serialize( writer, map );
    writer.WriteEndElement();
    writer.Close();
  }
}

/*
  // 
  // does not work - but see http://msdn.microsoft.com/en-us/magazine/cc164135.aspx
  // 
  public class Map : IXmlSerializable
  {
    const string NS = "http://xml.apache.org/xml-soap";
    public IDictionary dictionary;
    public Map()
    {
      dictionary = new Hashtable();
    }

    public Map(IDictionary dictionary)
    {
      this.dictionary = dictionary;
    }

    public void WriteXml(XmlWriter w)
    {
      w.WriteStartElement("Map", NS);
      foreach (object key in dictionary.Keys)
      {
        object value = dictionary[key];
        w.WriteStartElement("item", NS);
        w.WriteElementString("key", NS, key.ToString());
        w.WriteElementString("value", NS, value.ToString());
        w.WriteEndElement();
      }
      w.WriteEndElement();
    }

    public void ReadXml(XmlReader r)
    {
      r.Read(); // move past container
      r.ReadStartElement("dictionary");
      while (r.NodeType != XmlNodeType.EndElement)
      {
        r.ReadStartElement("item", NS);
        string key = r.ReadElementString("key", NS);
        string value = r.ReadElementString("value", NS);
        r.ReadEndElement();
        r.MoveToContent();
        dictionary.Add(key, value);
      }
    }
    public System.Xml.Schema.XmlSchema GetSchema() { return null; }
  }
*/

Sample output from Mono:

<q1:Map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1" d2p1:arrayType="Item[2]" xmlns:d2p1="http://schemas.xmlsoap.org/soap/encoding/" xmlns:q1="http://xml.apache.org/xml-soap">
  <Item href="#id2" />
  <Item href="#id3" />
</q1:Map>
[...]

Sample output from .NET:

<q1:Array xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="id1" q1:arrayType="Item[2]" xmlns:q1="http://schemas.xmlsoap.org/soap/encoding/">
  <Item href="#id2" />
  <Item href="#id3" />
</q1:Array>
[...]
Stefan Paletta
  • 378
  • 1
  • 7
  • your answer is the best one i have seen so far despite that it doesn't work on .net ... if one could just understand why.. i will give you the bounty for the effort you made. thank you. – Matan L May 05 '13 at 13:12
2

Either you can use KeyValue pair class or Dictionary class as follows:

        Dictionary<string, string> d = new Dictionary<string, string>();
        d.Add("apiKey", "******");
        var response = new object[] { d };

        KeyValuePair<string, string> d = new KeyValuePair<string, string>("apiKey", "******");
        var response = new object[] { d };
Santosh Panda
  • 7,235
  • 8
  • 43
  • 56
  • {"The type System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=]] was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."} – Matan L Apr 25 '13 at 20:05
  • this is the exception i got... fiddler didnt capture any request sent our of my computer at all .. – Matan L Apr 25 '13 at 20:06
  • 1
    I tried all possible way but could not find a way to consume the service due to the object[] @params. I would suggest you can change your route: Create a PHP Webservice app which will internally call the above webservice, as given above. From your .Net application consume the 1st Service and send the require arguments. Now you can crack this tough Nut. Hope so it would be helpful for you. – Santosh Panda May 04 '13 at 17:27
  • i agree that this is a solution and this is what i would do unless i have another way, but it freaks me out that its only a web service and i cant get an honest response from it .... there must be a way using only .net – Matan L May 05 '13 at 09:06
  • Actually the above Webservice is not a standard webservice & does not follow the WS-I webservice specification. You can use this online SoapClient to test the service: http://www.soapclient.com/soaptest.html While it can fetch/invoke the methods but can't construct the parameters. You can see the parameter list is empty. But if you'll try some other public webservice you can pass the parameters. I would rather suggest contact their support team or choose other route as I mentioned in previous comment. – Santosh Panda May 05 '13 at 09:35
0

It appears the problem is the difference between array in PHP and C#. In PHP, its a key value pair. How doest his look in the generated class that was created from the wdsl? Here is a line to a SO question that is related to your issue. C# equivalent to php associative array One answer says to try Dictionary<String, String>. Maybe be worth a try to use KeyValuePair<String, String> passed as an object array.

KeyValuePair<String, String> parm = new KeyValuePair<String, String>("apiKey","******");

var response = service.getTransactions(new object[] { parm });
Community
  • 1
  • 1
Scott Adams
  • 410
  • 3
  • 11
  • @MatanL, this is a tough one. I think this will boil down to hand altering the wdsl before you import to help it create types that will work with this A – Scott Adams Apr 25 '13 at 20:29