Archive

Archive for February, 2008

foreach Tops the Charts

February 28, 2008 1 comment

One of things that intrigues me about this blog are the search terms that drive readers here. As a writer, I get immense enjoyment when a particular article generates a lot of traffic, especially if it is one I feel passionate about or put a lot of effort into. Conversely, when these articles do not draw a lot of interest, I sometimes get discouraged. But alas, such is the life of the modern blogger.

The search terms should be a pretty good indication of what people are trying to learn about, although these are limited to topics already covered on the blog, so I don’t necessarily use them to determine future articles. I do use them, along with comments, trackbacks, pings, links, and digg submissions, as indicators to determine how I’m doing. I also get some enjoyment out of the process, and even the occasional ego boost (and we could all use that, right?)

So recently, I’ve been noticing a trend, and I thought I would share yesterdays “top ten search terms” with you:

  1. c# foreach
  2. foreach c#
  3. linq examples
  4. class diagram visual studio 2008
  5. c# xml linq
  6. filmstrip control c#
  7. localapp +c#
  8. uninstall previous version and install n
  9. +linq +example
  10. c# linq to xml write xml

I find a couple of interesting things about this, especially when I analyze the numbers associated with each term. First, while 4 of the 10 are LINQ related, they only account for 23% of the actual searches [and 63% of the LINQ searches were XML specific, which makes sense because the LINX to XML article is the most visited article in the history of this site]. What surprises me, though, is that the top 2 searches both regarding the humble C# Foreach, account for over 53% of the searches.

And this is not an isolated incident. In fact, given recent trends, I was surprised to see only 2 entries regarding foreach: usually there are 3-5! So what does it all mean? Well, it tells me that there are still a lot of people out there trying to get comfortable with .NET 2.0, even though most stuff being written about today (including by yours truly) is focused on .NET 3.5.

I take two things away from that. At first, I am discouraged that there are so many developers so far behind. This is a symptom that my original niche (AS/400 RPG Programming) has suffered from for a very long time: developers refusing to keep up with new language enhancements. I’m not gong to get into a lot of the whys, I have been witness to too many holy wars over such topics [VB6 vs. VB.NET comes immediately to mind], but I will say that this is a long term mistake driven by short term thinking.

The second thing I take away from it is much more encouraging: there are plenty of developers out there trying to improve. They are taking the necessary steps to adopt and implement newer technologies, and in .NET land few steps are as important as moving from 1.x technologies to 2.x+. Without a firm foundation in 2.0 technologies, moving to 3.x is virtually impossible. Over the past year or so, Microsoft has released a tidal wave of new technologies, tools, and features: knowing 2.0 is your life raft. If you don’t know 2.0 yet, I suggest you make learning and using it your most immediate professional priority.

Categories: C# 2.0

Trudging through ASP.NET

February 19, 2008 Comments off

I just wanted to post a quick update on what’s going on in my world this week.? I posted last week about my Authorize.Net efforts (which I updated this morning).? That work is the precursor to finally learning ASP.NET, one of my New Year’s Resolutions.? I have to admit that I have always assumed that learning ASP.NET would be no big deal.? I have developed web sites in PHP, JSP and Servlets, and even RPG CGI, so what could be so hard about ASP.NET?

Well, the short answer is that it is not hard, however I find myself constantly frustrated because I feel like I have to relearn all the basics.? Seemingly simple matters take me a lot of time and investigation to get working.? I’m sure that if I was learning from scratch this would be no problem, but in my case I am hobbled by too much knowledge going in.? In other words, my brain keeps getting in the way.

So I am trudging through books, websites, and training materials.? Currently, I am reading through Sitepoint.com’s “Build your own ASP.NET 2.0 Web Site“, which appears to be the simplest material I have on hand.? I’m also planning on spending some time going through some beginners videos at asp.net.? And if all that fails, I’ll consider ordering again from TotalTraining.com. I know one day soon I’ll look back on this and wonder what all the fuss was about, but for now I must simply “Soldier On!”

Authorize.net C# Code

February 13, 2008 19 comments

UPDATE 2: The final code for this project is available for download on the Free Code page.


UPDATE: The code for this posting was updated 2-19-2008. I removed the x_password reference. It is not listed in the current documentation, and according to Authorize.Net, the request needs to include the API Login ID and the TransactionCode instead. I also changed isTestMode to IsTestMode for consistency. I also confirmed with them that I need to submit the request from an SSL page, so I am still waiting for that before I can test the code.


My company has an account with Authorize.net, and I am in the process of using it to replace our old PayPal system for online Credit Card purchases. We’ve used their Virtual Terminal system for several years, and once long ago I integrated their CC processing with a PHP site.

I guess it has to do with supplying a ubiquitous solution, but I find that their developer integration tools are somewhat lacking. They have several different methods of submitting and processing payments, and I believe we used SIM (Server Integration Method) previously. In this solution, also my first official foray into the world of ASP.NET, we will be using AIM (Advanced Integration Method) which gives us a much finer level of control over the user experience.

There is a developer’s guide and sample code, but I found the sample code to be lacking in anything terribly useful, like a “sample”. It does not really show you how to send the request or handle the response. I assumed correctly that I am not the first person who wanted a better example, so I hit the Google pavement and found this little gem from 2004 at The Jackol’s Den.

This code gave me a great start, but in looking at it I decided I wanted something a little more flexible. This sample shows all the code in a single method which I assume is supposed to be embedded in an ASP.NET page. I would like to have something more reusable, so I restructured the code a bit.

First, there were a number of items hard coded, such as the Authorize.net version and account information. I could see where you might want this information to be stored in a database or configuration file somewhere, so I put the account information a separate class.

public class AuthNetAccountInfo
{
    public string AuthNetVersion {get; set;}
    public string AuthNetLoginID { get; set; }
    // public string AuthNetPassword { get; set; }
    public string AuthNetTransKey { get; set; }
    public bool IsTestMode { get; set; }
}

Second, some of the actual charge information was hard coded while the rest were passed in to the method as parameters. One item that was hard coded was the description, meaning that this approach would only be good for a single item (either that or the description had to be overly generic). Another is the card number and expiration date! Surely anyone would recognize that this could not go to production, but making it ready for prime time would again mean tying this method to a single page. Parameters such as first name, last name, address, and amount are passed in making the signature of the method long and a little unwieldy. So I created a TransactionRequestInfo class to handle all of these details.

public class TransactionRequestInfo
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
    public string Description { get; set; }

    private decimal _amount;
    public decimal ChargeAmount
    {
        get
        {
            return _amount;
        }
        set
        {
            _amount = Decimal.Round(value, 2);
        }
    }

    private string _zip;
    public string Zip
    {
        get
        {
            return _zip;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                if (res > 99999)
                {
                    throw new ArgumentException("Zip Code Value invalid");
                }
                else
                {
                    _zip = res.ToString().PadLeft(5, '0');
                }
            }
            else
            {
                throw new ArgumentException("Zip code must be numeric");
            }
        }

    }

    private string _securityCode;
    public string SecurityCode
    {
        get
        {
            return _securityCode;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                if (res > 999)
                {
                    throw new ArgumentException("Security Code Value invalid");
                }
                else
                {
                    _securityCode = res.ToString().PadLeft(3, '0');
                }
            }
            else
            {
                throw new ArgumentException("Security code must be numeric");
            }
        }

    }

    private string _cardNumber;
    public string CardNumber
    {
        get
        {
            return _cardNumber;
        }
        set
        {
            long res;
            if (long.TryParse(value, out res))
            {
                _cardNumber = res.ToString();
            }
            else
            {
                throw new ArgumentException("Card Number may only contain numbers");
            }
        }
    }

    private string _expDate;
    public string ExpDate
    {
        get
        {
            return _expDate;
        }
        set
        {
            int res;
            if (int.TryParse(value, out res))
            {
                string exp = res.ToString().PadLeft(4, '0');
                int month = int.Parse(exp.Substring(0, 2));
                int yr = int.Parse(exp.Substring(2, 2));

                if (yr > DateTime.Now.Year ||
                    (yr == DateTime.Now.Year && month >= DateTime.Now.Month))
                {
                    _expDate = month.ToString().PadLeft(2, '0') +
                        "/" + yr.ToString().PadLeft(2, '0');
                }
                else
                {
                    throw new ArgumentException("Expiration Date already passed");
                }
            }
            else
            {
                throw new ArgumentException("Zip code must be numeric");
            }
        }
    }
}

Finally, the original method returned a Boolean indicating whether or not the charge was successful. It does a lot of work deciphering and interpreting the errors and building good error messages, but it writes the message to a Label control. Again, this code is intended for a single page. I felt that we needed a real response mechanism, so I created another class for that as well.

public class TransactionResponseInfo
{
    public string AuthorizationCode { get; set; }
    public string TransactionID { get; set; }
    public string ReturnCode { get; set; }
    public string Message { get; set; }
}

Finally, I made a Transaction class to hold the method and made the method static. As you’ll see, most of the original code remains intact, with the necessary changes to incorporate the new class structure discussed above.

public static class Transaction
{
    public static TransactionResponseInfo ProcessPayment(
        TransactionRequestInfo transaction, AuthNetAccountInfo account)
    {
        TransactionResponseInfo response = new TransactionResponseInfo();

        WebClient objRequest = new WebClient();
        System.Collections.Specialized.NameValueCollection objInf =
          new System.Collections.Specialized.NameValueCollection(30);
        //System.Collections.Specialized.NameValueCollection objRetInf =
        //  new System.Collections.Specialized.NameValueCollection(30);
        byte[] objRetBytes;
        string[] objRetVals;
        string strError;

        #region Set Request Values
        objInf.Add("x_version", account.AuthNetVersion);
        objInf.Add("x_delim_data", "True");
        objInf.Add("x_login", account.AuthNetLoginID);
        // objInf.Add("x_password", account.AuthNetPassword);
        objInf.Add("x_tran_key", account.AuthNetTransKey);
        objInf.Add("x_relay_response", "False");
        objInf.Add("x_delim_char", ",");
        objInf.Add("x_encap_char", "|");

        // Billing Address
        objInf.Add("x_first_name", transaction.FirstName);
        objInf.Add("x_last_name", transaction.LastName);
        objInf.Add("x_address", transaction.Address);
        objInf.Add("x_city", transaction.City);
        objInf.Add("x_state", transaction.State);
        objInf.Add("x_zip", transaction.Zip);
        objInf.Add("x_country", transaction.Country);

        // Card Details
        objInf.Add("x_card_num", transaction.CardNumber);
        objInf.Add("x_exp_date", transaction.ExpDate);

        // Authorization code of the card (CCV)
        objInf.Add("x_card_code", transaction.SecurityCode);

        objInf.Add("x_method", "CC");
        objInf.Add("x_type", "AUTH_CAPTURE");
        objInf.Add("x_amount", transaction.ChargeAmount.ToString());
        objInf.Add("x_description", transaction.Description);

        // Currency setting. Check the guide for other supported currencies
        objInf.Add("x_currency_code", "USD");

        if (account.IsTestMode)
        {
            // Pure Test Server
            objInf.Add("x_test_request", "True");
            objRequest.BaseAddress =
              "https://test.authorize.net/gateway/transact.dll";
        }
        else if (!account.IsTestMode)
        {
            // Actual Server
            objInf.Add("x_test_request", "False");
            objRequest.BaseAddress =
              "https://secure.authorize.net/gateway/transact.dll";
        }
        else
        {
            throw new Exception("Transaction Mode Invalid");
        }
        #endregion

        try
        {
            // POST request
            objRetBytes =
              objRequest.UploadValues(objRequest.BaseAddress, "POST", objInf);
            objRetVals =
              System.Text.Encoding.ASCII.GetString(objRetBytes).Split(",".ToCharArray());

            // Process Return Values
            response.ReturnCode = objRetVals[0].Trim(char.Parse("|"));

            if (objRetVals[0].Trim(char.Parse("|")) == "1")
            {
                // Returned Authorisation Code
                response.AuthorizationCode = objRetVals[4].Trim(char.Parse("|"));
                // Returned Transaction ID
                response.TransactionID = objRetVals[6].Trim(char.Parse("|"));
                strError = "Transaction completed successfully.";
            }
            else
            {
                // Error!
                strError = objRetVals[3].Trim(char.Parse("|")) + " (" +
                  objRetVals[2].Trim(char.Parse("|")) + ")";

                if (objRetVals[2].Trim(char.Parse("|")) == "44")
                {
                    // CCV transaction decline
                    strError += "Our Card Code Verification (CCV) returned " +
                      "the following error: ";

                    switch (objRetVals[38].Trim(char.Parse("|")))
                    {
                        case "N":
                            strError += "Card Code does not match.";
                            break;
                        case "P":
                            strError += "Card Code was not processed.";
                            break;
                        case "S":
                            strError += "Card Code should be on card but was not indicated.";
                            break;
                        case "U":
                            strError += "Issuer was not certified for Card Code.";
                            break;
                    }
                }

                if (objRetVals[2].Trim(char.Parse("|")) == "45")
                {
                    if (strError.Length > 1)
                        strError += "n";

                    // AVS transaction decline
                    strError += "Our Address Verification System (AVS) " +
                      "returned the following error: ";

                    switch (objRetVals[5].Trim(char.Parse("|")))
                    {
                        case "A":
                            strError += " the zip code entered does not match " +
                              "the billing address.";
                            break;
                        case "B":
                            strError += " no information was provided for the AVS check.";
                            break;
                        case "E":
                            strError += " a general error occurred in the AVS system.";
                            break;
                        case "G":
                            strError += " the credit card was issued by a non-US bank.";
                            break;
                        case "N":
                            strError += " neither the entered street address nor zip " +
                              "code matches the billing address.";
                            break;
                        case "P":
                            strError += " AVS is not applicable for this transaction.";
                            break;
                        case "R":
                            strError += " please retry the transaction; the AVS system " +
                              "was unavailable or timed out.";
                            break;
                        case "S":
                            strError += " the AVS service is not supported by your " +
                              "credit card issuer.";
                            break;
                        case "U":
                            strError += " address information is unavailable for the " +
                              "credit card.";
                            break;
                        case "W":
                            strError += " the 9 digit zip code matches, but the " +
                              "street address does not.";
                            break;
                        case "Z":
                            strError += " the zip code matches, but the address does not.";
                            break;
                    }
                }

            }
        }
        catch (Exception ex)
        {
            strError = ex.Message;
        }

        response.Message = strError;

        return response;
    }
}

One of the changes I made was the Test Mode handling. Previously, this was also hard coded, requiring the developer to uncomment certain code to make the service “live”. I felt it would be a good enhancement to make this code driven. This way, the test vs. live status can be switched on and off by the end user (or via configuration). The code uses System.Collections.Specialized.NameValueCollection objects extensively, which I wanted to upgrade to a generic Dictionary<string, string>, but the System.Net.WebClient methods required the old NameValueCollection.

I’m not ready to actually publish this code yet: I haven’t written the code to implement this and it still needs to be tested. If I understand AIM, the request will need to come from a server that has an SSL Certificate installed, which as of yet I do not have. {UPDATE: I rechecked the documentation and it says if you have SSL you “may” use AIM, so perhaps this is optional}

Once I have it all working, I will add it to the Free Code section. In the meantime, feel free to try what is here and let me know your thoughts.

First RVNUG Presentation

February 8, 2008 Comments off

Last night I had the pleasure of presenting the program at the monthly RVNUG meeting. I spoke about the new Language Features in C# 3.0 and .NET 3.5. The material came from the first three articles in the “Upgrade Your C# Skills” series. I received lots of nice comments, so many thanks to those of you who were there.

You can download the slides of the presentation (Office 2007 format), but most of the demonstration was done writing live code in VS2008. I will be giving the same presentation at the Roanoke Code Camp on March 1st, so if you can make it be sure to say hello!

Categories: Miscellaneous

Great new CSS Reference

February 1, 2008 Comments off

I’ve been doing web development for about ten yearsIl regole poker ? un gioco di carte. now and have been a big fan of CSS since about 2000.? I’ve also been a big fan of SitePoint publications since they put out their first book, and I still buy most of their books even though I don’t do as much hands on web development as I used to.

In their blog they recently announced their new free online CSS reference, so naturally I went right over and checked it out.? The site is very well done and has the high level of quality I have come to expect from them. The navigation is good and easy to follow, and the individual property pages are well written.? Each one includes an example and descriptions of all the possible values as well as a grid that shows how well the feature is supported by the various browsers.

If you develop for the web at all, this is a site you must have in your bookmarks.

Categories: Web Development
Follow

Get every new post delivered to your Inbox.