Blog

VBA class for CommWeb Virtual Payment Client

on May 26 in blog posted , , , , , , , , , , by admin

A simple VBA class (Version 1.0) that will let you process 2-Party credit card transactions securely through the CommonWealth Bank’s CommWeb VPC merchant facility. This is primarily for MOTO transactions (Mail Order / Telephone Order) where the purchaser isn’t directly involved in the purchase/refund.

You need to include the Microsoft XML 4.0 library in your references options for this to work out of the box.

I also use a public module called settings that provides global access to system wide variables like settings.merchant_id, I think that part is pretty self explanatory.

Option Compare Database
Option Explicit

Private vpc_version As String               '1-8
Private vpc_command As String               '1-16
Private vpc_merch_txn_ref As String         '1-40
Private vpc_access_code As String           '8
Private vpc_merchant As String              '1-16
Private vpc_order_info As String            '1-34
Private vpc_amount As Long                  '1-10
Private vpc_card_num As String              '15-40
Private vpc_card_exp As String              '4
Private vpc_user As String                  'used to refund
Private vpc_password As String              'used to refund
Private vpc_transaction_no As String        'used to refund

Private vrs_authorise_id As String
Private vrs_transaction_no As String
Private vrs_response_code As String
Private vrs_receipt_no As String
Private vrs_txn_response_code As String
Private vrs_message As String
Private vrs_merch_txn_ref As String         '1-40
Private vrs_order_info As String            '1-34
Private vrs_amount As Long                  '1-10

Private h As MSXML2.ServerXMLHTTP40
Private errorExists As Boolean
Private message As String
Private exception As String

Private Sub Class_Initialize()

    Set h = New MSXML2.ServerXMLHTTP40

    vpc_version = "1"
    vpc_merchant = settings.merchant_id
    vpc_access_code = settings.moto_username
    vpc_user = settings.creditcard_refund_username
    vpc_password = settings.creditcard_refund_password

End Sub

Private Function refund_data() As String

    Dim buf As String
    buf = "vpc_Version=" & vpc_version & "&vpc_Command=refund"
    buf = buf & "&vpc_MerchTxnRef=" & vpc_merch_txn_ref
    buf = buf & "&vpc_AccessCode=" & vpc_access_code
    buf = buf & "&vpc_Merchant=" & vpc_merchant
    buf = buf & "&vpc_TransNo=" & vpc_transaction_no
    buf = buf & "&vpc_Amount=" & vpc_amount
    buf = buf & "&vpc_User=" & vpc_user
    buf = buf & "&vpc_Password=" & vpc_password

    refund_data = buf

End Function

Private Function payment_data() As String

    Dim buf As String
    buf = "vpc_Version=" & vpc_version & "&vpc_Command=pay"
    buf = buf & "&vpc_AccessCode=" & vpc_access_code
    buf = buf & "&vpc_Amount=" & vpc_amount
    buf = buf & "&vpc_CardExp=" & vpc_card_exp
    buf = buf & "&vpc_CardNum=" & vpc_card_num
    buf = buf & "&vpc_Merchant=" & vpc_merchant
    buf = buf & "&vpc_OrderInfo=" & URLEncode(vpc_order_info)
    buf = buf & "&vpc_MerchTxnRef=" & URLEncode(vpc_merch_txn_ref)

    payment_data = buf

End Function

Public Function process_transaction(cc_number As String, cc_exp As Date, Amount As Currency, our_ref As String) As Boolean

    'pre-load
    process_transaction = True

    'prepare
    vpc_merch_txn_ref = our_ref
    vpc_card_num = cc_number
    vpc_card_exp = Format(cc_exp, "yymm")
    vpc_amount = (Round(Amount, 2) * 100)

    'process
    If Not Err Then
        h.Open "POST", "https://migs.mastercard.com.au/vpcdps", False
        h.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        h.Send payment_data()

        If Len(h.responseText) = 0 Then

            errorExists = True
            message = "No response from the Virtual Payment Client (The Bank)"
            process_transaction = False
            Exit Function

        End If

    End If

    'parse results
    parse_results

    'post-transaction clean up
    If errors_returned() Then

        process_transaction = False
        Exit Function

    End If

End Function

Public Function refund_transaction(our_ref As String, transaction As String, Amount As Currency) As Boolean

    'pre-load
    refund_transaction = True

    'prepare
    vpc_command = "refund"
    vpc_merch_txn_ref = our_ref
    vpc_transaction_no = transaction
    vpc_amount = (Round(Amount, 2) * 100)
    vrs_authorise_id = "0"                      'for those times when the provider doesn't give and auth number

    'process
    If Not Err Then

        h.Open "POST", "https://migs.mastercard.com.au/vpcdps", False
        h.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        h.Send refund_data()

        If Len(h.responseText) = 0 Then

            errorExists = True
            message = "No response from the Virtual Payment Client (The Bank)"
            refund_transaction = False
            Exit Function

        End If

    End If

    'parse results
    parse_results

    'post-transaction clean up
    If errors_returned() Then

        refund_transaction = False
        Exit Function

    End If

End Function

Private Sub parse_results()

'    The content returned by the VPC is a HTTP POST, so the content will
'    be in the format parameter1=value&parameter2=value&parameter3=value,
'    i.e. key/value pairs separated by ampersands "&".

    Dim results() As String
    Dim pair() As String
    Dim i As Long

    results = Split(h.responseText, "&")

    For i = 0 To UBound(results)
        pair = Split(results(i), "=")
        Select Case (pair(0))
        Case "vpc_Amount"
            vrs_amount = CCur(pair(1)) / 100

        Case "vpc_AuthorizeId"
            vrs_authorise_id = pair(1)

        Case "vpc_ReceiptNo"
            vrs_receipt_no = pair(1)

        Case "vpc_transactionNo"
            vrs_transaction_no = pair(1)

        Case "vpc_AcqResponseCode"
            vrs_response_code = pair(1)

        Case "vpc_TxnResponseCode"
            vrs_txn_response_code = pair(1)

        Case "vpc_Message"
            vrs_message = URLDecode(pair(1))

        Case Else
            'I'm not interested in other fields at this stage
            'MsgBox "unexpected pair: " & results(i)

        End Select
    Next

End Sub

Private Function errors_returned() As Boolean

    errors_returned = True

    If Len(vrs_txn_response_code) = 0 Then
        ' Display an Error Page as the QSIResponseCode could not be retrieved
        message = "(23) No result for this field: 'TxnResponseCode'" & vbCrLf & "Bank responded: " & vrs_message
    Else

        Select Case vrs_txn_response_code
        Case "0"  'Transaction Succesful

        Case "1"  'Unknown Error
            message = "Transaction could not be processed (Unknown Error)"

        Case "2", "E"  'Bank Declined Transaction
            message = "Bank declined transaction (Customer should contact their Bank)"

        Case "3"  'No reply from Bank
            message = "No reply from Bank"

        Case "4"  'Expired Card
            message = "Expired Card"

        Case "5"  'Insufficient Funds
            message = "Insufficient Funds in Account"

        Case "6"  'Error Communicating with Bank
            message = "Error communicating with Bank"

        Case "7"  'Payment Server System Error"
            message = "Payment Server system error"

        Case "8"  'Transaction Type not supported
            message = "Transaction type not supported"

        Case "9"  'Bank Declined Transaction
            message = "Bank Declined Transaction"

        Case Else
            message = "Unknown Error: " & vrs_txn_response_code
        End Select

        ' Check if the result contains an error message
        If vrs_txn_response_code <> "0" Then
            Dim result As String
            ' Get the error returned from the Payment Client
            result = vrs_message
            ' check if result contains a value
            If Len(result) <> 0 Then
                ' there is an error message so generate an Error Page
                exception = result
            End If
        Else
            errors_returned = False
        End If
    End If

End Function

Public Function get_result_transaction_id() As String
    get_result_transaction_id = vrs_transaction_no
End Function

Public Function get_result_amount() As Currency
    get_result_amount = vrs_amount
End Function

Public Function get_result_receipt() As String
    get_result_receipt = vrs_receipt_no
End Function

Public Function get_authorise_id() As String
    get_authorise_id = vrs_authorise_id

End Function

Public Function show_errors() As String
    If Len(message) Then
        show_errors = message
        If Len(exception) > 0 Then
            show_errors = message & " - " & exception
        End If
    Else
        show_errors = "There was an error but no messages for it"
    End If
End Function

Private Sub Class_Terminate()

    Set h = Nothing

End Sub

simple auto complete text field

on Mar 26 in blog posted , , , , by admin

I have been working on understanding the text_field_with_auto_complete functionality in Ruby on Rails for a project I am currently working on. There seems to be two written tutorials on the subject and they were a little confusing. Here is my attempt to be more specific.

Goal:

While editing an address record I want to show the user a short list of available suburbs that match what they have begun typing into he text box; and allow them to pick the one they want to use. When they save the record, I only want to store the ID of the suburb they typed in, not the actual text.

Solution:

Install the auto_complete gem:

ruby script/plugin install git://github.com/rails/auto_complete.git

This didn’t work on the windows system I was using, so I ended up having to grab the ZIP file and copy the contents to the ‘vendor/plugins/auto_complete’ folder the above command created.

First of all add to the site.rb model so we can refer to the suburb_name as if it was a field in the sites table:

def suburb_name
  suburb.name if suburb_id
end

def suburb_name=(value)
  self.suburb_id = Suburb.find_by_name(value.split(',')[0]).id unless value.blank?
end

Update the sites/edit.html.erb view to do the auto_complete markup:

<p>
  <%= f.label :suburb %><br />
  <%= text_field_with_auto_complete :site, :suburb_name, {}, :skip_style => false %>
</p>

Add an action to the sites_controller.rb so the auto_complete call(s) can get the short list:

def auto_complete_for_site_suburb_name()  
  @suburbs = Suburb.find(:all , :conditions=> "name like '%"+params[:site][:suburb_name].upcase+"%' and we_deliver_to=1")  
  render :partial => 'auto_complete_suburb_name'
end

Finally, create the _auto_complete_suburb_name.html.erb partial in the views/sites folder, the smallest form of which is like this

<ul><% for suburb in @suburbs do %><li><%=h suburb.name %>, <%=h suburb.postcode %></li><% end %></ul>

Important stuff you should know

  • I have added highlighting to illustrate the important connections between names as you move between view/model/controller. If you change one, you need to make the same change in every other occurence.
  • The suburbs table has no duplicates across the [name] field and no commas in any name value.
  • The COMMA in the partial is vitally important, I use it to tell the difference between the name value and the rest of the information that I don’t care about (the postcode). Use your own unique seperator if you have comma’s in your data.
  • No spaces in the _auto_complete_suburb_name partial, if you format it nicely your selected result will have lots of extra spaces (you’ll see what I mean)
  • There is no error checking in this. If the user types in a junk suburb the current code will not handle it gracefully.

Facebook fanfare

on Feb 15 in blog posted , , , , , by admin

Facebook. What’s a company to do? It’s not a person, doesn’t have friends and rarely goes to parties.

  • Can your company have a Facebook page? Yes.
  • Can your company have friends? Not really, Companies can have fans (People who like it).
  • Can your company sell products on Facebook? No.

So what’s the point?

Facebook is tricky. You have to look at your company a little differently. You have to look at it from the point of view of the people who are happy (really happy) with your product or service. You have to convince them there is some social benefit to allowing you to send them messages directly. You can’t sell product on Facebook, you give away popularity.

For example: Let’s say company FRS makes shoes. Fancy shoes. Fancy RED Shoes. The kind of shoes that stand out; shoes that people buy so that when other people look at them they go “WOW! Look at those shoes”. Facebook loves FRS. Facebook is designed for FRS to post endless successions of images of beautiful red shoes that it’s fans can say “I want these” or “These are Soooooooo beautiful”. Get the idea?

But your company isn’t like FRS, your company sells lunches in the CBD. What can you do? Post the afternoon specials before 11:30 every morning. Tell your fans what’s for lunch. Make them want it. MAKE THEM WANT IT!

Facebook is ALL about generating comments. Positive comments, obviously, are more delightful than negative ones, but negative comments can be good too. Answer the negative comments. Tell your fans what you are going to do about them. Improve your company. Publicly.

Prove the company to the fans >> get more fans >> generate more interest >> get more fans >> make more happy customers >> get more fans >> build your fan base >> Get. More. Fans.

If your company is in the business of making people happy (with what they bought from you) then you can benefit from a Facebook page. If that’s not true (and you need to be honest here) then it’s probably a good idea to check Facebook for hate pages with your name on them. Do something about that. It’s free market research with the customers name on it.

Better business through Report Server

on Dec 22 in blog posted , , by admin

I have been having a fantasic time creating reports for Microsoft’s Report Server. It’s actually been very usefull for the company to be able to have PDF versions of various regular reports being automatically emailed to the people who are supposed to be reading them.

The upshot of this automation is that the reports that haven’t been well thought out are quickly brought in to focus and revised by the people who use them. The number of reports that are being sent out has dropped and the information contained in the remainder are salient and accurate.

This translates into fewer people reading shorter reports which allows more time for smart decisions to be made within the company.

Process evolution

on Dec 05 in blog posted , , by admin

With every new release of an upgrade/update comes a new opportunity to re-examine the processes that lead to it’s development. As the software developer I can only refined the automatic processes that lie between the human interactions. I cannot click the button that starts the next step, I can only perform what comes after the button click – up to when another button needs to be clicked.

When the new release is made, a business should also be thinking about whether or not that button needs to be clicked in the same way, or for the same reasons.

This way the business processes are advancing effectively.

Express yourself

on Nov 18 in blog posted , , , , by admin

I’ve just spent two hours talking with my developer about his understanding of The System for a new project. It was an excellent learning experience. I was given a lesson in technical writing for someone else. Where I thought I was being detailed, I was confusing the issues.

Now I have a better understanding of what is a good descriptive method for him. With this information I’ll be updating the existing requirements document, for my internal use. It’s an iterative process that will never end, but well worth the trip.

What’s programming like?

on Nov 04 in blog posted , by admin

I came across this Independant Programmer article (via Jeff’s SQL Server Blog) which tries to explain what it is like In The Zone for a programmer.

It even neatly describes an interrupt routine (boom boom) for non-programmer types, like your boss, to use when they need to get in the way. Possibly worth forwarding on to the higher ups who are prepared to take on a little personal development (or HR management).

Gooey G.U.I.

on Oct 15 in blog posted , , by admin

I found an interesting concept for “The Next User Interface”, over at TechCrunch, called CON10UUM.

What I like about it is that from start to finish, they have sculpted a new method of interaction with our machines. It’s a pity that we are so attached to our keyboards, but for the moment doing without them is probably too big of a change for anyone to take too seriously. While we continue to depend on the written word for most of our computer based business interactions we can’t do away with them yet, although voice recognition is the next logical replacement it’s not quite there yet (apparantly).

CON10UUM is certainly food for thought and there are products out there that already perform similar tasks (Mac touchpads, Wacom Bamboo Touch) but they really only add a thin set of commands to the existing U.I.s that we already use.

Involve the client

on Oct 13 in blog posted , , , by admin

I was talking to one of my developers a few days ago, about how I like to demonstrate completion of The Design Phase.

Having a face to face discussion about the project’s design is a good way to start, but having an interactive presentation (model) to help them “feel” their product is orders of magnitude better. When it’s interactive the client is engaged with the product early and is able to experience their product prior to development. This way I can quickly and simply prove that I was listening when they were telling me what they wanted (during the Requirements Phase).

And with a model of version (n) in front of them, the client can begin to think clearly about version (n+1).

Because everyone in this industry knows; A requirements document is never a perfect description of what the client wants, it’s the best version this time around.

Business Cards

on Oct 06 in blog posted by admin

I was sent an invitation to trial a new product about a week ago: miniture plastic business cards and I thought I would just quickly share my excitement about it.

In terms of a user interface, the website was beautiful. So simple. So elegant. Such a great User Experience. Have a look yourself – www.squizcards.com

The cards are actually really great too, but that’s bye-the-bye for me. What got me in and kept me fascinated, was the ability to quickly customise someone elses designs and make it my own. Hats off to the designers, excellent work!