RSS Feed

Banking with XML

Posted on Sunday, March 1, 2009 in Practical Programming

By Peter Jones

peter_jones Remember XML? Remember those fun times you had creating XML data files, XSL’s and DTD’s using Notepad? It seemed like such a great thing to do. There were grandiose plans to save the world with markup.

Today, XML is just another file format thanks to great tools and (reasonably well followed) standards. The tools in Visual Studio do such a great job of encapsulating XML that we often forget that it even exists under the covers.

And then along comes a scenario where you need to dig deep and reach for those reference manuals (or search engine). Recently I had just such a challenge.

The Mission

Create a site for Students and Law Professionals to register for seminars offered by a law college. Seminar attendees would pay for seminars using a credit card. Payments would be processed online by the college’s bank.

The Solution

The web site was created using ASP.NET MVC, Visual Studio 2008 and SQL Server 2005. The college’s bank provided various documents to describe their payment gateway interface which supports payment by the usual credit cards, plus credit transactions and other services. For this site we only required the payment service.

Principals

The .NET Framework is huge. It contains over 11,000 types (classes, structs, etc). You can bet your bottom dollar that there are several ways to do just about any task you choose. My overriding principal when creating any coded solution is to use the clearest and easiest to maintain code. Performance and elegance normally come second to this – mostly because of the types of applications I create but also because simple code is usually better code.

Getting Setup

For this exercise I have created a sample web service and client web site to test it. You can download the complete sample code from here. This sample includes a complete standalone project for processing payments with a test payment gateway. With a bit of work you should be able to easily adapt this for a real payment gateway of your choice.

In the remainder of this article I will be focusing on the PaymentGatewayFormatter class from the sample. A full explanation of the sample code is beyond the scope of this article but please feel free to drop me a line if you have any questions or suggestions.

The Problem

In these enlightened times, we tend to expect ’standard’ web services to be made available for just about everything. Connecting to such a web service is trivial if you can use Visual Studio or the .NET SDK WSDL tool to generate the interface proxy for you. However, in this case, the bank provided only the following:

  • A REST style web service sending and receiving plain old XML (POX) with no WSDL.
  • A PDF document describing the service architecture, rules for usage, sample XML request/response messages, and DTD schemas.
  • A PDF document describing the HTTP/XML and Java interfaces.

And that was it – nothing for .NET, no XSD, no WSDL and no SOAP. A call to the bank requesting a .NET interface resulted in a one word answer, which you can probably guess!

The Implementation

After some research and experimentation with Windows Communication Foundation (WCF) and Web Service Contract First (WSCF) and with limited time to create the interface, I opted for a hand coded implementation using low level framework support and HTTPWebRequest.

The Specification

The bank provided sample request and response messages as below:

Sample Request:

xml version="1.0" encoding="UTF-8"?>
<TransactMessage>
  <MessageInfo>
    <messageID>8af793f9af34bea0cf40f5fb5c630c</messageID>
  </MessageInfo>
  <MerchantInfo>
    <merchantID>123456</merchantID>
    <password>password</password>
  </MerchantInfo>
  <RequestType>Payment</RequestType>
  <Payment>
    <TxnList count="1">
      <Txn ID="1">
        <txnType>0</txnType>
        <amount>1000</amount>
        <purchaseOrderNo>test</purchaseOrderNo>
        <CreditCardInfo>
          <cardNumber>4444333322221111</cardNumber>
          <expiryDate>08/12</expiryDate>
        </CreditCardInfo>
      </Txn>
    </TxnList>
  </Payment>
</TransactMessage>

Sample Response:

xml version="1.0" encoding="UTF-8"?>
<TransactMessage>
  <MessageInfo>
    <messageID>b9cffb095b4f460fb0d60be85975b600</messageID>
  </MessageInfo>
  <RequestType>Payment</RequestType>
  <Status>
    <statusCode>000</statusCode>
    <statusDescription>Normal</statusDescription>
  </Status>
  <MerchantInfo>
    <merchantID>123456</merchantID>
  </MerchantInfo>
  <Payment>
    <TxnList count="1">
      <Txn ID="1">
        <txnType>0</txnType>
        <amount>1000</amount>
        <purchaseOrderNo>test</purchaseOrderNo>
        <approved>Yes</approved>
        <responseCode>00</responseCode>
        <responseText>Approved</responseText>
        <txnID>009844</txnID>
        <CreditCardInfo>
          <pan>444433...111</pan>
          <expiryDate>08/12</expiryDate>
          <cardType>6</cardType>
          <cardDescription>Visa</cardDescription>
        </CreditCardInfo>
      </Txn>
    </TxnList>
  </Payment>
</TransactMessage>

My first inclination was to create XSD files using these samples. Visual Studio made this easy to achieve. With the sample XML files open in Visual Studio I selected the Create Schema option from the XML menu. This produced an accurate XSD file. However, this was not much use by itself. I needed to create a C# class to include in my web site that would enable serialization of XML to and from the payment gateway.

The command line WSDL tool provided in the .NET Framework SDK does a great job of generating code for just this purpose. But in this case it was not so useful. Note that the request and response messages both have a root node of . When I generated the C# classes from the generated XSD files, I had duplicate classes in the files for TransactionMessage, MessageInfo, Status etc. Fixing this would have required hand coding of the schema to include request and response messages, or merging of the generated classes. This felt like a lot of error-prone work.

XmlTextWriter

All I really wanted was a simple way to send a bunch of XML to a web site and read the response. The XML was not particularly complex or large so I opted to create the request using XmlTextWriter.

XmlTextWriter could not be any easier. I’ll explain how to use it in the steps below.

1)   Create a method to compose the required XML. I use a custom object, PaymentRequest to encapsulate the details of the request.

public string GetPaymentRequestXML(PaymentRequest request)

(All of the following code belongs inside this method).

2)   Create a stream for the XmlTextWriter to write to.

MemoryStream ms = new MemoryStream();

3)   Create an instance of an XmlTextWriter. Note the use of UTF8 encoding to match the sample XML file.

XmlTextWriter writer = new XmlTextWriter(ms, System.Text.Encoding.UTF8);

4)   Use the XmlTextWriter to write elements and attributes to the stream. XmlTextWriter provides methods that produce validated elements only. For your example, we begin with the root node, TransactMessage:

writer.WriteStartElement("TransactMessage");

Every element that is started must be ended:

writer.WriteEndElement(); // TransactMessage

I find it less error prone to always create the start and end element method calls in pairs, and build the structure from the outside in – much like matching curly-braces in C#. Adding a comment to the WriteEndElement method call will also help you remember what you are ending. For example:

writer.WriteStartElement("MessageInfo"); 
writer.WriteElementString("messageID",
                          request.MessageID.ToString("N")); 
writer.WriteEndElement(); // MessageInfo

This will produce the following XML in the output stream:

<MessageInfo>
<messageID>8af793f9af34bea0cf40f5fb5c630c</messageID>
</MessageInfo> 

Use WriteElementString to create a complete element with a simple string as its inner xml value. To create an attribute on the current element simply follow the WriteStartElement call with WriteAttributeString.

writer.WriteStartElement("TxnList"); 
writer.WriteAttributeString("count", "1"); 

5)   Flush the writer.

writer.Flush();

6)   Convert the stream to a string and return it.

string xml = null
using (StreamReader sr = new StreamReader(ms)) 
{

  ms.Seek(0, SeekOrigin.Begin); 
  xml = sr.ReadToEnd(); 

return xml; 

And that’s about it. XmlTextWriter has many more useful methods to deal with namespaces and other standard XML features, but for this simple example these are not required.

XmlReader

Writing an XML file is very simple. Reading one, unfortunately, is not quite as straight forward. The main issue is that you cannot assume the order of elements will totally match your expectations. In our Bank of Jones sample it may be quite feasible to receive a response with the MessageInfo, Status and MerchantInfo elements in any order.

XmlReader allows you to read elements in an expected order, in much the same way as you would write them with XmlTextWriter. This may work for a while but when dealing with external systems there is no guarantee that it will work forever. In fact, when dealing with banks, it’s a very good idea to assume it will change at random and you will only know about this when your client code fails!

Fortunately, there is a way to deal with this. XmlReader allows you to processes the XML serially. An example is the best way to describe this. Here are the steps.

1)   Create an empty method to process the response stream.

public PaymentResponse ReadPaymentResponse(Stream input) 
{

}

2)   Create an XmlReader instance, and somewhere to store the de-serialized response data. In my case this is a PaymentResponse object.

PaymentResponse rsp = new PaymentResponse(); 
XmlReader reader = XmlReader.Create(input); 
rsp.Status = PaymentResponse.StatusEnum.Incomplete;

3)   Read through the input stream using reader.Read() which will read through the input XML one ‘item’ at a time. The item could be a start element, an attribute, an end element, comment, white space and more. Each time you call .Read() it advanced the internal pointer of the reader to the next item and sets a number of properties that you can use to determine exactly what the reader is pointing at. This is a very efficient way of parsing large XML files/streams because it is a forward only, read only mechanism.

Below is the main code I use to process the input stream.

while (reader.Read())

{

  if (reader.NodeType == XmlNodeType.Element)

  {

    if (reader.Name == "TransactMessage")

    {

      while (reader.Read())

      {

        if (reader.NodeType == XmlNodeType.Element &&

        reader.Name == "MessageInfo")

        {

          readMessageInfo(rsp, reader);

        }

        else if (reader.NodeType == XmlNodeType.Element &&

                 reader.Name == "RequestType")

        {

          reader.Read();

          rsp.RequestType = reader.Value;

        }

        else if (reader.Name == "MerchantInfo")

        {

          readMerchantInfo(rsp, reader);

        }

        else if (reader.Name == "Status")

        {

          readStatus(rsp, reader);

        }

        else if (reader.Name == "Payment")

        {

          readPayment(rsp, reader);

        }

      }

    }

  }

}

This code uses a number of sub-processing methods that also advance the cursor through the input stream (I have excluded these from here for brevity but you can examine them in the sample source code provided). Breaking the processing method into smaller chunks like this makes the methods much clearer.

Below is the helper method to update the PaymentResponse with the CreditCardInfo node. The other processing methods follow the same pattern of looping until the node ends.

private static void readCardInfo(PaymentResponse rsp, XmlReader reader)

{

  while (reader.Read())

  {

    if (reader.NodeType == XmlNodeType.Element)

    {

      if (reader.Name == "pan")

      {

        reader.Read();

        rsp.CardNumber = reader.Value;

      }

      else if (reader.Name == "expiryDate")

      {

        reader.Read();

        rsp.Expiry = reader.Value;

      }

      else if (reader.Name == "cardType")

      {

        reader.Read();

        rsp.CardType = reader.Value;

      }

      else if (reader.Name == "cardDescription")

      {

        reader.Read();

        rsp.CardDescription = reader.Value;

      }

    }

    else if (reader.NodeType == XmlNodeType.EndElement &&

             reader.Name == "CreditCardInfo")

    {

      break;

    }

  }

}

Alternatives

The .NET Framework provides several ways of reading and writing XML files. The ‘legacy’ method is to use XmlDocument. This still works well but tends to be rather inefficient, especially with large XML files. The construction of the XML with XmlDocument is simple but not as intuitive as with XmlTextWriter. Reading of data from XmlDocument generally requires navigating through the structure using XPath queries.

If the 3.5 version of the .NET Framework, the new XDocument provided with LINQ is an excellent option. Using XDocument I could construct the payment transaction thus:

XDocument doc = new XDocument(

  new XDeclaration("1.0", Encoding.UTF8.HeaderName, string.Empty),

    new XElement("TransactMessage",

      new XElement("MessageInfo",

        new XElement("messageID", request.MessageID.ToString("N"))

      ),

      new XElement("MerchantInfo",

        new XElement("merchantID", MerchantID),

        new XElement("password", Password)

      ),

      new XElement("RequestType", PAYMENT_REQUEST_TYPE),

      new XElement("Payment",

        new XElement("TxnList",

          new XAttribute("count", "1"),

          new XElement("Txn",

            new XAttribute("ID", "1"),

            new XElement("txnType", "0"),

            new XElement("amount",

              (request.Amount * 100).ToString("##########")),

            new XElement("purchaseOrderNo", request.OrderNumber),

            new XElement("CreditCardInfo",

              new XElement("cardNumber", request.CardNumber),

              new XElement("expiryDate", request.Expiry),

              new XElement("cvv", request.CVV)

            )

          )

        )

      )

    )

  );

This syntax is certainly easy to construct and read but as you can imagine, a large deeply nested XML file can look messy and finding a mismatched parenthesis or missing comma is not fun.

Reading XML with LINQ bypasses the need to create cryptic XPath queries but it is still a query driven process. For example, to read the MessageInfo node would require the following code:

var messageInfo = (from mi in ele.Descendants("MessageInfo"
                   select new 
                   { 
                    messageID = mi.Element("messageID").Value 
                   }).SingleOrDefault(); 

This code is clear enough and in some ways safer than my example, but it is not available for .NET 2.0. For my situation, XmlTextWriter and XmlReader provided a clear, simple, and efficient mechanism for dealing with XML data.

Peter Jones is a 25 year veteran (vintage?) programmer and Microsoft MVP (ASP.NET) from New Zealand. He currently works for Intergen – a leading Microsoft Gold Partner. Peter has worked on and created systems for many industries such as Telecommunications, Manufacturing, Government, Education and more. He cut his programming teeth on dBase 3, Clipper 5 and C before moving to Windows development tools such as Visual Basic, Delphi and .NET. Currently he is working with content management systems (SharePoint and EPiServer) in Sydney, Australia. You can contact Peter and read his occasionally updated blog at http://jonesie.net.nz.

Be the first to comment.

`

Bad Behavior has blocked 180 access attempts in the last 7 days.