Sample Code for Posting XML to QuickBooks Legacy QBMS Payments API

A lot has been written about exchanging data with REST APIs as it seems most modern APIs prefer to accept JSON as the preferred format.  Nevertheless often developers are at the mercy of the API they are communicating with as was the case for a recent project I did for the River of Time Museum in Fountain Hills, Arizona.

This blog post is kind of specific but for anyone who needs assistance connecting their e-commerce application to QuickBooks Legacy QBMS Payments API where JSON is not supported, I hope it helps.

A Bit of History on the Project…

The River of Time Museum is a small museum next to the Fountain Hills Library and this past fall it closed its doors for two months for renovations looking to introduce the public to a new set of museum exhibits.  The museum did a nice job making the public aware of its temporary closure.  But what most people probably didn’t know was that during that time the museum also decided to shut down and re-brand it website as well.

And in December 2015 with the grand re-opening, the museum launched www.rotmuseum.org which besides a fresh new look, also included full e-commerce functionality to sell museum tickets, memberships and event dinner reservations online as well as a backend content management system that split website administration roles amongst the different museum volunteers who run it daily.

I was honored that Museum Director Luke Bate selected my proposal to build the new site.  Luke and his team were great to work with and the end result couldn’t have turned out better.  The new site was modern, mobile-friendly and I was able to incorporate a turnkey e-commerce solution in time for the museum’s grand re-opening in early 2016.

New Website Created for River of Time Museum
In December The River of Time Museum launched a new website that featured its new exhibits and allowed visitors to purchase admission tickets and group tours online.

View New Website

API Project Challenges

But that’s not to say the project didn’t have its challenges… mainly one specific museum requirement that all orders placed through the website needed to be integrated with the museum’s QuickBooks software.

My initial thought was that such a requirement was a good idea and smart thing for Luke to put in the RFP.

But during development I quickly learned that the museum used an older version of QuickBooks and that volunteers were so comfortable using it, that it wasn’t really feasible to upgrade.  That in turn meant that credit card processing on the new website needed to be connected to a legacy QuickBooks merchant account which required API integration to a legacy rest API (QuickBooks Legacy QBMS Payments).

Surprisingly documentation on connecting and submitting transactions to this REST API was somewhat limited from QuickBooks side.  Only XML format was accepted and below is the code I ultimately used to process credit card transactions.

XML Message (Request and Response Pairs)

Important Note: the XML values below for Application Login, Connection Ticket and App ID are pre-assigned by QuickBooks and will come from your QuickBooks merchant account. TransRequestID is a system generated number you would provide specific to your web application and e-commerce store.

string XMLmessage = "<?xml version=\"1.0\"?>";
XMLmessage = XMLmessage + "<?qbmsxml version=\"4.1\"?>";
XMLmessage = XMLmessage + "<QBMSXML>
 ";
 XMLmessage = XMLmessage + "<SignonMsgsRq>
 ";
 XMLmessage = XMLmessage + "<SignonDesktopRq>
 ";
 XMLmessage = XMLmessage + "<ClientDateTime>2016-01-21T21:54:30</ClientDateTime>";
 XMLmessage = XMLmessage + "<ApplicationLogin>-- App Login --</ApplicationLogin>";
 XMLmessage = XMLmessage + "<ConnectionTicket> -- Connection Ticket ---</ConnectionTicket>";
 XMLmessage = XMLmessage + "<Language>English</Language>";
 XMLmessage = XMLmessage + "<AppID>-- APP ID --</AppID>";
 XMLmessage = XMLmessage + "<AppVer>1.0</AppVer>";
 XMLmessage = XMLmessage + "
 </SignonDesktopRq>";
 XMLmessage = XMLmessage + "
 </SignonMsgsRq>";
 XMLmessage = XMLmessage + "<QBMSXMLMsgsRq>
 ";
 XMLmessage = XMLmessage + "<CustomerCreditCardChargeRq>
 ";
 XMLmessage = XMLmessage + "<TransRequestID>292929</TransRequestID>";
 XMLmessage = XMLmessage + "<CreditCardNumber>1111111111114444</CreditCardNumber>";
 XMLmessage = XMLmessage + "<ExpirationMonth>11</ExpirationMonth>";
 XMLmessage = XMLmessage + "<ExpirationYear>2016</ExpirationYear>";
 XMLmessage = XMLmessage + "<IsCardPresent>false</IsCardPresent>";
 XMLmessage = XMLmessage + "<Amount>10.00</Amount>";
 XMLmessage = XMLmessage + "<NameOnCard>John Doe</NameOnCard>";
 XMLmessage = XMLmessage + "<CreditCardAddress>1234 W. Elm</CreditCardAddress>";
 XMLmessage = XMLmessage + "<CreditCardPostalCode>90210</CreditCardPostalCode>";
 XMLmessage = XMLmessage + "<SalesTaxAmount>1.00</SalesTaxAmount>";
 XMLmessage = XMLmessage + "
 </CustomerCreditCardChargeRq>";
 XMLmessage = XMLmessage + "
 </QBMSXMLMsgsRq>";
 XMLmessage = XMLmessage + "
</QBMSXML>";

C# XML Post and Receive Response

In the code  below I included both the Quickbooks QBMS production and test gateway URLs.  When posting the XML, it is required to set the ContentType = “application/x-qbmsxml”.

using System;
using System.Xml;

var url = "https://merchantaccount.quickbooks.com/j/AppGateway"; // Production Gateway URL
// var url = "https://merchantaccount.ptc.quickbooks.com/j/AppGateway"; // Test Gateway URL

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
byte[] bytes;
bytes = System.Text.Encoding.ASCII.GetBytes(XMLmessage);
request.ContentType = "application/x-qbmsxml";
request.ContentLength = bytes.Length;
request.Method = "POST";
Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);
requestStream.Close();

var getStatusCode = ""; // Quickbooks Transaction Status Code
var approvedTransaction = "";   // checks stausCode Node.  Will return a value of 0 if transaction is approved.  

// If there is a failure, there will be a non-zero statusCode and a statusMessage attribute will be included with additional information.

var transactionMessage = "";
var getStatusMessage = "";

var transactionID = "";

HttpWebResponse response;
response = (HttpWebResponse)request.GetResponse();
    if (response.StatusCode == HttpStatusCode.OK)
        {
            Stream responseStream = response.GetResponseStream();
            XmlDocument xml = new XmlDocument();
            xml.Load(responseStream);
            XmlNodeList xnList = xml.SelectNodes("/QBMSXML/SignonMsgsRs/SignonDesktopRs");
            foreach (XmlNode chldNode in xnList)
            {
                getStatusCode = chldNode.Attributes["statusCode"].Value;
                if (getStatusCode != "0")
                {
                    getStatusMessage = chldNode.Attributes["statusMessage"].Value;
                }
            }
            if (getStatusCode == "0")
            {
                XmlNodeList xnList2 = xml.SelectNodes("/QBMSXML/QBMSXMLMsgsRs/CustomerCreditCardChargeRs");
                foreach (XmlNode chldNode in xnList2)
                {
                    approvedTransaction = chldNode.Attributes["statusCode"].Value; /// value of 0 means transaction is approved
                    if (approvedTransaction != "0")
                    {
                        transactionMessage = chldNode.Attributes["statusMessage"].Value;
                    }
                }
            }
        }

Additional Resoureces