Flash->XML-RPC->Magento

In this article I’ll show you how to connect (login) and disconnect (release session) to the magento store using Magento API.

I assume that you have working magento store somewhere (local or online).First lets define the access to the store through the web services

  • go to Admin area, System->Web Services->Roles (System->Usługi Sieciowe->Role użytkowników)
  • click on Add New Role (Dodaj nową rolę)
  • Type in “Role Name” (“Nazwa roli”) field desired name for new role then go to the Role Resources (Zasoby roli) tab in the left hand pane.
  • Select all resources that you wish to be enabled for the user with this role (enabled means callable through web-service).

I have got one “admin” role and I have applied all resources to it. Of course I can foresee that granular restriction would be more safe and in the end application I will set depending on the application and the suer itself, i.e.  the sales guy doesn’t need to have access to, let say, “Catalog”.

  • OK, once we have selected required resources, click on Save Role button.

Create our remote user

  • go to the System->Web Services->Users (System->Usługi Sieciowe->Użytkownicy)
  • Click on Add New user (Dodaj nowego użytkownika)
  • In the User Info (Info użytkownika) fill all required fields, they are pretty self explanatory except one:) Api Key – basically it is the password, you will use it along with User Name (in API documentation as Api User) to login to the magento.
  • If you will do this go to the User Role tab in the left hand pane, and choose the role for this user (and through this what resources are available for this user). Once done click on Save User (Zapisz użytkownika).

Now the real meat:) some code, this is the just proof-of-concept, quick and dirty.


package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;

	/**
	 * Sample Code to show the login/logout functionality of the Magento API
	 * @author Lukasz 'Severiaan' Grela
	 */
	public class Main extends Sprite
	{
		public static const LOGIN:String = "login";
		public static const API_USER:String = "YOUR_API_USER";//i.e.RemoteUser
		public static const API_KEY:String = "YOUR_API_KEY";//i.e.RemoteUserPass
		public static const GATEWAY:String = "YOUR_MAGENTO_STORE_URL/api/xmlrpc/";//i.e. http://127.0.0.1/magento/api/xmlrpc/

		public function Main():void
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			//lets create our login request
			var _xRequest:XML =<methodCall>
								  <methodName>{Main.LOGIN}</methodName>
								  <params>
									<param>
									  <value>{Main.API_USER}</value>
									</param>
									<param>
									  <value>{Main.API_KEY}</value>
									</param>
								  </params>
								</methodCall>;

			//now we need to communicate with the webservice, to do this we need to use
			var _oReq:URLRequest = new URLRequest(Main.GATEWAY);
				_oReq.contentType = "text/xml";
				_oReq.data = _xRequest;
				trace(_oReq.data);
				_oReq.method = URLRequestMethod.POST;

			var _oClient:URLLoader = new URLLoader();

				_oClient.addEventListener(ProgressEvent.PROGRESS, onProgress, false, 0, true);
				_oClient.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
				_oClient.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
				_oClient.addEventListener(Event.COMPLETE, onResponse, false, 0, true);

				_oClient.load(_oReq);

		}

		/**
		 * Something went wrong and here we will tell about it to the user.
		 * @param	errorMsg
		 */
		protected function onFault(errorMsg:String):void
		{
			trace(errorMsg);
		}
		/**
		 *
		 * @param	response
		 */
		protected function onSuccess(response:XML):void
		{
			//this still doesn't meant that we have success, it merely indicate that URLLoader returned some values, if we are here it is XML formatted.
			if (response.fault.length() > 0)
			{
				onFault(response.fault.value.toXMLString());
			}
			else
			{
				if (response.params.param.length() == 1)
				{
					trace(response.params.param.value);
				}
				else
				{
					//incorrect response format
					onFault(response.toXMLString());
				}
			}
		}

		/**
		 * Event.COMPLETE handler, this is the first point to decide if we have success or failure after getting any response.
		 * @param	e
		 */
		protected function onResponse(e:Event):void
		{
			//wrapped with try as this might throw error when data returned is not a valid XML
			try
			{
				onSuccess(new XML(e.target.data));
			}
			catch (err:*)
			{
				onFault("Invalid response data\n"+e.target.data);
			}
		}
		/**
		 * SecurityErrorEvent.SECURITY_ERROR handler, this is the show stopper, it means that you can't access the remote resource (i.e. flash cannot load any data from different domain).
		 * @param	e
		 */
		protected function onSecurityError(e:SecurityErrorEvent):void
		{
			onFault("Security Error: "+e);
		}
		/**
		 * IOErrorEvent.IO_ERROR handler, this is the show stopper, it means that the remote resource is inaccessible, i.e. you have misspelled the URL, or it doesn't exist and so on:)
		 * @param	e
		 */
		protected function onIOError(e:IOErrorEvent):void
		{
			onFault("IO Error: "+e);
		}
		/**
		 * ProgressEvent.PROGRESS handler, you may show to the user the progress for the call (loader or something)
		 * @param	e
		 */
		protected function onProgress(e:ProgressEvent):void
		{
			//progress useful for large responses
			trace(e);
		}

	}
}

after running this code you should have similar result:

<methodCall>
  <methodName>login</methodName>
  <params>
    <param>
      <value>RemoteUser</value>
    </param>
    <param>
      <value>RemoteUserPass</value>
    </param>
  </params>
</methodCall>
[ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=169 bytesTotal=169]
<value>
  <string>35389ae00d8fc47b69e5c42e613fb25a</string>
</value>
 

OK, so it means that we have access granted, but also you may see something different, i.e.


<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value>
            <int>2</int>
          </value>
        </member>
        <member>
          <name>faultString</name>
          <value>
            <string>Access denied.</string>
          </value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>
 

So this is the fault response that you may see when your user doesn’t exist, password is not right. So let’s handle success now.
we have one handler for success and another for failure, we need to make it more usable as at the moment we will not be able to say, for which command we received response (successful or not).

Simple variable that will hold currently invoked command will do:

  protected var m_sCurrentCommand:String;

To make this example more usable I have modified further code from previous version:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	
	/**
	 * Sample Code to show the login/logout functionality of the Magento API
	 * @author Lukasz 'Severiaan' Grela
	 */
	public class Main extends Sprite
	{
		public static const LOGIN:String = "login";
		public static const END_SESSION:String = "endSession";
		
		public static const API_USER:String = "YOUR_API_USER";//i.e.RemoteUser
		public static const API_KEY:String = "YOUR_API_KEY";//i.e.RemoteUserPass
		public static const GATEWAY:String = "YOUR_MAGENTO_STORE_URL/api/xmlrpc/";//i.e. http://127.0.0.1/magento/api/xmlrpc/

		protected var m_sSessionId:String;
		protected var m_oLogIn:CustomSimpleButton;
		protected var m_oLogOut:CustomSimpleButton;
		protected var _oReq:URLRequest;
		protected var _oClient:URLLoader;
		protected var m_sCurrentCommand:String;
		
		
		public function Main():void
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			m_oLogIn = new CustomSimpleButton(100, 22, 0x008000);
			m_oLogOut = new CustomSimpleButton(100, 22, 0xFA0000);
			

			//now we need to communicate with the webservice, to do this we need to use
			_oReq = new URLRequest(Main.GATEWAY);
				_oReq.contentType = "text/xml";
				_oReq.method = URLRequestMethod.POST;
				
			_oClient = new URLLoader();
			
				_oClient.addEventListener(ProgressEvent.PROGRESS, onProgress, false, 0, true);
				_oClient.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
				_oClient.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
				_oClient.addEventListener(Event.COMPLETE, onResponse, false, 0, true);
			
				
			addChild(m_oLogIn);
			addEventListener(MouseEvent.CLICK, onClick, false, 0, true);
				//
			
		}
		/**
		 * Log in
		 */
		protected function login():void
		{
			m_sCurrentCommand = Main.LOGIN;
			//lets create our login request
			var _xRequest:XML = <methodCall>
								  <methodName>{m_sCurrentCommand}</methodName>
								  <params>
									<param>
									  <value>{Main.API_USER}</value>
									</param>
									<param>
									  <value>{Main.API_KEY}</value>
									</param>
								  </params>
								</methodCall>;
			//
			_oReq.data = _xRequest;
			//
			trace("login()");
			trace(_oReq.data);
			//
			_oClient.load(_oReq);
			
		}
		/**
		 * Log out
		 */
		protected function logout():void
		{
			//we need to create another request, this time we will use endSession method
			m_sCurrentCommand = Main.END_SESSION;
			//one note, so far we have covered only string data type in XML-RPC, this is the only one type that is allowed to skip it's type node, i.e. <string>abc</string> is equal to abc alone.
			var _xRequest:XML = <methodCall>
								  <methodName>{m_sCurrentCommand}</methodName>
								  <params>
									<param>
									  <value>{m_sSessionId}</value>
									</param>
								  </params>
								</methodCall>;
			//
			_oReq.data = _xRequest;
			//
			trace("logout()");
			trace(_oReq.data);
			//
			_oClient.load(_oReq);
				//hide button to dissallow multiple click:-)
				if (contains(m_oLogOut)) removeChild(m_oLogOut);
		}
		
		/**
		 * Something went wrong and here we will tell about it to the user.
		 * @param	errorMsg
		 */
		protected function onFault(errorMsg:String):void
		{
			switch (m_sCurrentCommand)
			{
				case Main.LOGIN:
					//handle login error, i.e. show login panel again
				break;
				case Main.END_SESSION:
					//handle end session error, i.e. do nothing:) as it possibly was caused by session expired, or trying to end alread ended session
				break;
				default:
			}
			trace("Call to " + m_sCurrentCommand + " was unsuccessfull.");
			trace(errorMsg);
		}
		/**
		 * we have received an xml in response
		 * @param	response
		 */
		protected function onSuccess(response:XML):void
		{
			trace(response);
			//this still doesn't meant that we have success, it merely indicate that URLLoader returned some values, if we are here it is XML formatted.
			if (response.fault.length() > 0)
			{
				onFault(response.fault.value.toXMLString());
			}
			else
			{
				if (response.params.param.length() == 1)
				{
					switch (m_sCurrentCommand)
					{
						case Main.LOGIN:
							//ok so we have success
							//response to the login command is the sessionid of type string, we can access it here like this, we need to store it as it will be used with any other calls.
							m_sSessionId = response.params.param.value.string;
							
							trace("login returned:" + (response.params.param.value.string));
							//show logout button//green
							addChild(m_oLogOut);
							//remove login//red
							if (contains(m_oLogIn)) removeChild(m_oLogIn);
						break;
						case Main.END_SESSION:
							//endSession returns Boolean value
							trace("endSession returned:" + (response.params.param.value.boolean));
							//
							addChild(m_oLogIn);
							//
							if (contains(m_oLogOut)) removeChild(m_oLogOut);
						break;
						default:
					}
					//clear command
					m_sCurrentCommand = "";
				}
				else
				{
					//incorrect response format
					onFault(response.toXMLString());
				}
			}
		}
		
		
		/**
		 * MouseEvent.CLICK handler
		 * @param	e
		 */
		protected function onClick(e:MouseEvent):void
		{
			switch (e.target)
			{
				case m_oLogIn:
					login();
				break;
				case m_oLogOut:
					logout();
				break;
			}
		}
		
		
		/**
		 * Event.COMPLETE handler, this is the first point to decide if we have success or failure after getting any response.
		 * @param	e
		 */
		protected function onResponse(e:Event):void
		{
			//wrapped with try as this might throw error when data returned is nto a valid XML
			try
			{
				onSuccess(new XML(e.target.data));
			}
			catch (err:*)
			{
				onFault("Invalid response data\n"+e.target.data);
			}
		}
		/**
		 * SecurityErrorEvent.SECURITY_ERROR handler, this is the showstopper, it means that you can't access the remote resource (i.e. flash cannot load any data from different domain).
		 * @param	e
		 */
		protected function onSecurityError(e:SecurityErrorEvent):void
		{
			onFault("Security Error: "+e);
		}
		/**
		 * IOErrorEvent.IO_ERROR handler, this is the showstopper, it meanst that the remote resource is inaccessible, i.e. you have misspelled the URL, or it doesn't exist and so on:)
		 * @param	e
		 */
		protected function onIOError(e:IOErrorEvent):void
		{
			onFault("IO Error: "+e);
		}
		/**
		 * ProgressEvent.PROGRESS handler, you may show to the user the progress fo the call (loader or something)
		 * @param	e
		 */
		protected function onProgress(e:ProgressEvent):void
		{
			//progress usefull for large responses
			trace(e);
		}
		
	}
}
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.SimpleButton;

class CustomSimpleButton extends SimpleButton
{
    private var upColor:Number   = 0xFFCC00;
    private var overColor:Number = 0xCCFF00;
    private var downColor:Number = 0x00CCFF;

    public function CustomSimpleButton(width:Number, height:Number, baseColor:Number)
	{
		upColor = baseColor;
        downState      = new ButtonDisplayState(downColor, width, height);
        overState      = new ButtonDisplayState(overColor, width, height);
        upState        = new ButtonDisplayState(upColor, width, height);
        hitTestState   = new ButtonDisplayState(upColor, width, height);
        hitTestState.x = 0;
        hitTestState.y = 0;
		
        useHandCursor  = true;
    }
}

class ButtonDisplayState extends Shape
{
    private var bgColor:Number;
    private var _width:uint;
    private var _height:uint;

    public function ButtonDisplayState(bgColor:Number, width:Number, height:Number)
	{
        this.bgColor = bgColor;
        _width = width;
        _height = height;
        draw();
    }

    private function draw():void
	{
        graphics.beginFill(bgColor);
        graphics.drawRect(0, 0, _width, _height);
        graphics.endFill();
    }
}
 

This code will generate following output (on success:)

Click on log in button (green)


login()
<methodCall>
  <methodName>login</methodName>
  <params>
    <param>
      <value>remoteUser</value>
    </param>
    <param>
      <value>remoteUserPass</value>
    </param>
  </params>
</methodCall>
 

receving response


[ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=169 bytesTotal=169]
<methodResponse>
  <params>
    <param>
      <value>
        <string>95d69fef8ca3257da28d6d5bdbbe3b1d</string>
      </value>
    </param>
  </params>
</methodResponse>
login returned:95d69fef8ca3257da28d6d5bdbbe3b1d
 

clicking on log out button (red)


logout()
<methodCall>
  <methodName>endSession</methodName>
  <params>
    <param>
      <value>95d69fef8ca3257da28d6d5bdbbe3b1d</value>
    </param>
  </params>
</methodCall>
 

receiving response


[ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=140 bytesTotal=140]
<methodResponse>
  <params>
    <param>
      <value>
        <boolean>1</boolean>
      </value>
    </param>
  </params>
</methodResponse>
endSession returned:1
 

That is all for now, happy coding:)

This entry was posted in flash, magento, xml-rpc and tagged , , , , . Bookmark the permalink.