Introduction
The following article is a response to a number of requests I had from my Students to show them how to intervene in the way a class produces an XML serialized version, of an objects internal state, or how we get an XML document transformed into a type that easily allows us to manipulate it.
In a few words how we can change not the semantics of an object, but the syntax of the XML that is produced by the XMLSerializer class.
I have decided to use an actual project that I have developed recently and contain the code that allows an application to use with ease, some of the services of the WinBank PayCenter.
Background
When we want to use the services of WinBank XML Web Service, it is required by us, in our systems to collect all the information for a transaction. Then the above referenced transaction information must be sent in XML form, to the web service of PayCenter and receive a response again in XML for the successful or not completion of the transaction.
Transactions are send one at a time and the Schema of the Request document should derive from the following XML
REQUEST XML WEB SERVICE
<?xml version="1.0" encoding="ISO-8859-7"?>
<winbankpos>
<header>
<actiontype>
<transtype/>
<transname/>
<method/>
</actiontype>
<merchantinfo>
<merchantid/>
<posid/>
<channeltype/>
<user/>
</merchantinfo>
<errorcode/>
<errordescription/>
</header>
<body>
<transactioninfo>
<merchantreference/>
<currency/>
<amount/>
<installments/>
<preorderdate/>
<entrytype/>
<transactionid/>
</transactioninfo>
<cardinfo>
<track2/>
<emv/>
<cardtype/>
<cardnumber/>
<expirationmonth/>
<expirationyear/>
<cvv2/>
</cardinfo>
</body>
</winbankpos>
Τhe result of the transaction is immediately send to the Merchant in the following form RESPONSE XML
<?xml version="1.0" encoding="ISO-8859-7"?>
<winbankpos>
<header>
<actiontype>
<transtype/>
<transname/>
<method/>
</actiontype>
<merchantinfo>
<merchantid/>
<posid/>
<channeltype/>
<user/>
</merchantinfo>
<errorcode/>
<errordescription/>
</header>
<body>
<transactioninfo>
<transactionid/>
<merchantreference/>
<responsesuccessflag/>
<responsecode/>
<responsedescription/>
<approvalcode/>
<retrievalref/>
</transactioninfo>
</body>
</winbankpos>
Iιn order to start working we need to create a Class Library Project called WinBankLib that contain 2 top classes: WinBankRequest and WinBankResponse.
ΒΒoth when serialized follow the above corresponding XML signature.
BΒelow is the actual implementation of the WinBankRequest
‘ WinBankRequest.vb by Byron Thanopoulos
‘Import the necessary namespaces
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
‘ since we need to change the Root Element of this class when serialized we need to ‘apply declaratively some attributes
<XmlRoot("winbankpos")> Public Class WinBankRequest
<XmlElement("header")> Public header As HeaderClass = New HeaderClass
<XmlElement("body")> Public body As BodyClass = New BodyClass
Public Function GetResponse(ByVal url As String) As WinBankResponse
' Configure HTTP request
Dim httpRequest As HttpWebRequest
httpRequest = WebRequest.Create(url)
httpRequest.Method = "POST"
'Prepare correct encoding for XML Serialization
Dim encoding As UTF8Encoding = New UTF8Encoding
' Use Xml property to obtain serialized XML data
' convert into bytes using encoding specified above and get length
Dim bodybytes() As Byte = encoding.GetBytes(Xml)
httpRequest.ContentLength = bodybytes.Length
' Get HTTP Request stream for putting XML data into
Dim httpRequestBodyStream As Stream = httpRequest.GetRequestStream()
' Fill Request Stream with Serialized XML data
httpRequestBodyStream.Write(bodybytes, 0, bodybytes.Length)
httpRequestBodyStream.Close()
' Get HTTP response // the Request is actually initiated when we call the httpRequest.GetResponse
Dim httpResponseStream As StreamReader
httpRequest.Credentials = New System.Net.NetworkCredential("username", "password")
Dim httpResponse As HttpWebResponse = httpRequest.GetResponse()
httpResponseStream = New StreamReader(httpResponse.GetResponseStream(), System.Text.Encoding.ASCII)
Dim httpResponseBody As String
' Extract XML from Response
httpResponseBody = httpResponseStream.ReadToEnd
httpResponseStream.Close()
' Ignore everything that is NOT XML by removing Headers
httpResponseBody = httpResponseBody.Substring(httpResponseBody.IndexOf("<?xml"))
' Deserialize XML into WinBankResponse
Dim serializer As XmlSerializer = _
New XmlSerializer(GetType(WinBankResponse))
Dim ResponseReader As StringReader = New StringReader(httpResponseBody)
'********
' Return WinBankResponse
Return CType(serializer.Deserialize(ResponseReader), WinBankResponse)
End Function
<XmlIgnore()> Public ReadOnly Property Xml() ' Simply Serializes the WinBankRequest Instance into XML
Get
' Prepare Xml Serializer
Dim Serializer As XmlSerializer = New XmlSerializer(GetType(WinBankRequest))
' serialize into string builder
Dim bodyBuilder As StringBuilder = New StringBuilder
Dim bodyWriter As StringWriter = New StringWriter(bodyBuilder)
Serializer.Serialize(bodyWriter, Me)
'replace Utf-16 encoding with utf-8
Dim body As String = bodyBuilder.ToString
body = body.Replace("utf-16", "utf-8")
Return body
End Get
End Property
End Class
Following the same principals the WinBankResponse code will be
‘WinBankResponse.vb by Byron Thanopoulos
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
<XmlRoot("winbankpos")> Public Class WinBankResponse
<XmlElement("header")> Public header As HeaderClass = New HeaderClass
<XmlElement("body")> Public body As BodyClass = New BodyClass
<XmlIgnore()> Public ReadOnly Property Xml()
Get
' Prepare Xml Serializer
Dim Serializer As XmlSerializer = New XmlSerializer(GetType(WinBankResponse))
' serialize into string builder
Dim bodyBuilder As StringBuilder = New StringBuilder
Dim bodyWriter As StringWriter = New StringWriter(bodyBuilder)
Serializer.Serialize(bodyWriter, Me)
'replace Utf-16 encoding with utf-8
Dim body As String = bodyBuilder.ToString
body = body.Replace("utf-16", "utf-8")
Return body
End Get
End Property
End Class
Within both of the above documents there are more helper classes that their code is included as well in alphabetical order.
'ActionTypeClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class ActionTypeClass
<XmlElement("transtype")> Public transtype As String
<XmlElement("transname")> Public transname As String
<XmlElement("method")> Public method As String
End Class
'AssemblyInfo.vb
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("")>
<Assembly: AssemblyProduct("")>
<Assembly: AssemblyCopyright("")>
<Assembly: AssemblyTrademark("")>
<Assembly: CLSCompliant(True)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("A3C98D9F-7C6F-4B56-9F9C-D492C837F5B0")>
' Version information for an assembly consists of the following four values:
'
' Major Version
' Minor Version
' Build Number
' Revision
'
' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
<Assembly: AssemblyVersion("1.0.*")>
'BodyClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class BodyClass
<XmlElement("transactioninfo")> Public transactioninfo As TransactioninfoClass = New TransactioninfoClass
<XmlElement("cardinfo")> Public cardinfo As CardinfoClass = New CardinfoClass
End Class
'CardinfoClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class CardinfoClass
<XmlElement("track2")> Public track2 As String
<XmlElement("emv")> Public emv As String
<XmlElement("cardtype")> Public cardtype As String
<XmlElement("cardnumber")> Public cardnumber As String
<XmlElement("expirationmonth")> Public expirationmonth As String
<XmlElement("expirationyear")> Public expirationyear As String
<XmlElement("cvv2")> Public cvv2 As String
End Class
'HeaderClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class HeaderClass
<XmlElement("actiontype")> Public actiontype As ActionTypeClass = New ActionTypeClass
<XmlElement("merchantinfo")> Public merchantinfo As MerchantinfoClass = New MerchantinfoClass
<XmlElement("errorcode")> Public errorcode As String
<XmlElement("errordescription")> Public errordescription As String
End Class
'MerchantinfoClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class MerchantinfoClass
<XmlElement("merchantid")> Public merchantid As String
<XmlElement("posid")> Public posid As String
<XmlElement("channeltype")> Public channeltype As String
<XmlElement("user")> Public user As String
End Class
'TransactioninfoClass.vb
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class TransactioninfoClass
<XmlElement("merchantreference")> Public merchantreference As String
<XmlElement("currency")> Public currency As String
<XmlElement("amount")> Public amount As String
<XmlElement("installments")> Public installments As String
<XmlElement("preorderdate")> Public preorderdate As String
<XmlElement("entrytype")> Public entrytype As String
<XmlElement("transactionid")> Public transactionid As String
End Class
So now we have the objects that we need to initiate communication with PayCenter system and an example is shown below
‘WinBankTestform.vb
Imports winBankLib
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Xml.Serialization
Public Class WinBankTestform
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
Protected WithEvents TextBox1 As System.Web.UI.WebControls.TextBox
Protected WithEvents TextBox2 As System.Web.UI.WebControls.TextBox
Protected WithEvents TextBox3 As System.Web.UI.WebControls.TextBox
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Protected WithEvents Label2 As System.Web.UI.WebControls.Label
'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
TextBox1.Text = ""
TextBox2.Text = ""
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.Text = ""
TextBox2.Text = ""
' initialize variables
Dim request As WinBankRequest
Dim requestSerializer As XmlSerializer = New XmlSerializer(GetType(WinBankRequest))
Dim response As WinBankResponse
Dim responseSerializer As XmlSerializer = New XmlSerializer(GetType(WinBankResponse))
Dim xmlBuilder As StringBuilder
Dim xmlWriter As StringWriter
Dim winBankUrl As String = ConfigurationSettings.AppSettings("winBankVerificationUrl") '"https://Paycenter.winbank.gr/webservice/service.asp"
request = New WinBankRequest
request.header.actiontype.transtype = "10"
request.header.actiontype.transname = "CheckTransaction"
request.header.actiontype.method = "Request"
request.header.merchantinfo.merchantid = "xxxxxxxx"
request.header.merchantinfo.posid = " xxxxxxxx "
request.header.merchantinfo.channeltype = " xxxxxxxx "
request.header.merchantinfo.user = ConfigurationSettings.AppSettings("winBankVerificationPWD")
request.header.errorcode = ""
request.header.errordescription = ""
request.body.transactioninfo.merchantreference = Me.TextBox3.Text
request.body.transactioninfo.currency = ""
request.body.transactioninfo.amount = ""
request.body.transactioninfo.installments = ""
request.body.transactioninfo.preorderdate = ""
request.body.transactioninfo.entrytype = ""
request.body.transactioninfo.transactionid = ""
request.body.cardinfo.track2 = ""
request.body.cardinfo.emv = ""
request.body.cardinfo.cardtype = ""
request.body.cardinfo.cardnumber = ""
request.body.cardinfo.expirationmonth = ""
request.body.cardinfo.expirationyear = ""
request.body.cardinfo.cvv2 = ""
'display request
xmlBuilder = New StringBuilder
xmlWriter = New StringWriter(xmlBuilder)
requestSerializer.Serialize(xmlWriter, request)
Me.TextBox1.Text = xmlBuilder.ToString
' get response
response = request.GetResponse(winBankUrl)
If response.header.errorcode = "0" Then
Label2.BackColor = System.Drawing.Color.DeepSkyBlue
Label2.Text = "This OrderID has been succesfully processed by Win Bank"
Else
Label2.BackColor = System.Drawing.Color.White
Label2.ForeColor = System.Drawing.Color.Red
Label2.Text = "This OrderID has NOT been processed by Win Bank"
End If
xmlBuilder = New StringBuilder
xmlWriter = New StringWriter(xmlBuilder)
responseSerializer.Serialize(xmlWriter, response)
Me.TextBox2.Text = xmlBuilder.ToString
End Sub
End Class
This concludes my post.
I hope you will find it useful. Even though I could have produced the necessary classes using the XSD.exe I chose in this example to do it on my own. Please note that this is not a complete solution, but part of it. This does not change the fact that the above code is functional and you could use it straight away to connect your e-commerce application with PayCenter.
Ευχαριστώ πολύ
Θανόπουλος Βύρων – Friend of David Waterston – who ever knows him will know that that’s an honor.
Byron
MCAD .net, MCT, MCDBA Senior Developer/Team Lead