Skip to content

Commit

Permalink
[Gateway] Add new NABTransact(AU) Gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
tommeier committed Feb 29, 2012
1 parent c6d0115 commit 9b4462c
Show file tree
Hide file tree
Showing 5 changed files with 538 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
* [MerchantWare](http://merchantwarehouse.com/merchantware) - US
* [Modern Payments](http://www.modpay.com) - US
* [Moneris](http://www.moneris.com/) - CA
* [NABTransact](http://www.nab.com.au/nabtransact/) - AU
* [Netaxept](http://www.betalingsterminal.no/Netthandel-forside) - NO, DK, SE, FI
* [NetRegistry](http://www.netregistry.com.au) - AU
* [NELiX TransaX Gateway](http://www.nelixtransax.com) - US
Expand Down
244 changes: 244 additions & 0 deletions lib/active_merchant/billing/gateways/nab_transact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
require 'rexml/document'

module ActiveMerchant #:nodoc:
module Billing #:nodoc:
# The National Australia Bank provide a payment gateway that seems to
# be a rebadged Securepay Australia service, though some differences exist.
class NabTransactGateway < Gateway
API_VERSION = 'xml-4.2'
PERIODIC_API_VERSION = "spxml-4.2"

TEST_URL = 'https://transact.nab.com.au/test/xmlapi/payment'
LIVE_URL = 'https://transact.nab.com.au/live/xmlapi/payment'
TEST_PERIODIC_URL = "https://transact.nab.com.au/xmlapidemo/periodic"
LIVE_PERIODIC_URL = "https://transact.nab.com.au/xmlapi/periodic"

self.supported_countries = ['AU']

# The card types supported by the payment gateway
# Note that support for Diners, Amex, and JCB require extra
# steps in setting up your account, as detailed in the NAB Transact API
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]

self.homepage_url = 'http://transact.nab.com.au'
self.display_name = 'NAB Transact'

cattr_accessor :request_timeout
self.request_timeout = 60

self.money_format = :cents
self.default_currency = 'AUD'

#Transactions currently accepted by NAB Transact XML API
TRANSACTIONS = {
:purchase => 0, #Standard Payment
:credit => 4, #Refund
:void => 6, #Client Reversal (Void)
:authorization => 10, #Preauthorise
:capture => 11 #Preauthorise Complete (Advice)
}

PERIODIC_TYPES = {
:addcrn => 5,
:editcrn => 5,
:deletecrn => 5,
:trigger => 8
}

SUCCESS_CODES = [ '00', '08', '11', '16', '77' ]


def initialize(options = {})
requires!(options, :login, :password)
@options = options
super
end

def test?
@options[:test] || super
end

def purchase(money, credit_card_or_stored_id, options = {})
if credit_card_or_stored_id.is_a?(ActiveMerchant::Billing::CreditCard)
#Credit card for instant payment
commit :purchase, build_purchase_request(money, credit_card_or_stored_id, options)
else
#Triggered payment for an existing stored credit card
options[:billing_id] = credit_card_or_stored_id.to_s
commit_periodic build_periodic_item(:trigger, money, nil, options)
end
end

def store(creditcard, options = {})
requires!(options, :billing_id, :amount)
commit_periodic(build_periodic_item(:addcrn, options[:amount], creditcard, options))
end

def unstore(identification, options = {})
options[:billing_id] = identification
commit_periodic(build_periodic_item(:deletecrn, options[:amount], nil, options))
end

private

def build_purchase_request(money, credit_card, options)
xml = Builder::XmlMarkup.new
xml.tag! 'amount', amount(money)
xml.tag! 'currency', options[:currency] || currency(money)
xml.tag! 'purchaseOrderNo', options[:order_id].to_s.gsub(/[ ']/, '')

xml.tag! 'CreditCardInfo' do
xml.tag! 'cardNumber', credit_card.number
xml.tag! 'expiryDate', expdate(credit_card)
xml.tag! 'cvv', credit_card.verification_value if credit_card.verification_value?
end

xml.target!
end

#Generate payment request XML
# - API is set to allow multiple Txn's but currentlu only allows one
# - txnSource = 23 - (XML)

def build_request(action, body)
xml = Builder::XmlMarkup.new
xml.instruct!
xml.tag! 'NABTransactMessage' do
xml.tag! 'MessageInfo' do
xml.tag! 'messageID', Utils.generate_unique_id.slice(0, 30)
xml.tag! 'messageTimestamp', generate_timestamp
xml.tag! 'timeoutValue', request_timeout
xml.tag! 'apiVersion', API_VERSION
end

xml.tag! 'MerchantInfo' do
xml.tag! 'merchantID', @options[:login]
xml.tag! 'password', @options[:password]
end

xml.tag! 'RequestType', 'Payment'
xml.tag! 'Payment' do
xml.tag! 'TxnList', "count" => 1 do
xml.tag! 'Txn', "ID" => 1 do
xml.tag! 'txnType', TRANSACTIONS[action]
xml.tag! 'txnSource', 23
xml << body
end
end
end
end

xml.target!
end

def build_periodic_item(action, money, credit_card, options)
xml = Builder::XmlMarkup.new

xml.tag! 'actionType', action.to_s
xml.tag! 'periodicType', PERIODIC_TYPES[action] if PERIODIC_TYPES[action]
xml.tag! 'currency', options[:currency] || currency(money)
xml.tag! 'crn', options[:billing_id]

if credit_card
xml.tag! 'CreditCardInfo' do
xml.tag! 'cardNumber', credit_card.number
xml.tag! 'expiryDate', expdate(credit_card)
xml.tag! 'cvv', credit_card.verification_value if credit_card.verification_value?
end
end
xml.tag! 'amount', amount(money)

xml.target!
end

def build_periodic_request(body)
xml = Builder::XmlMarkup.new
xml.instruct!
xml.tag! 'NABTransactMessage' do
xml.tag! 'MessageInfo' do
xml.tag! 'messageID', ActiveMerchant::Utils.generate_unique_id.slice(0, 30)
xml.tag! 'messageTimestamp', generate_timestamp
xml.tag! 'timeoutValue', request_timeout
xml.tag! 'apiVersion', PERIODIC_API_VERSION
end

xml.tag! 'MerchantInfo' do
xml.tag! 'merchantID', @options[:login]
xml.tag! 'password', @options[:password]
end

xml.tag! 'RequestType', 'Periodic'
xml.tag! 'Periodic' do
xml.tag! 'PeriodicList', "count" => 1 do
xml.tag! 'PeriodicItem', "ID" => 1 do
xml << body
end
end
end
end

xml.target!
end

def commit(action, request)
response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, build_request(action, request)))

Response.new(success?(response), message_from(response), response,
:test => test?,
:authorization => authorization_from(response)
)
end

def commit_periodic(request)
response = parse(ssl_post(test? ? TEST_PERIODIC_URL : LIVE_PERIODIC_URL, build_periodic_request(request)))
Response.new(success?(response), message_from(response), response,
:test => test?,
:authorization => authorization_from(response)
)
end

def success?(response)
SUCCESS_CODES.include?(response[:response_code])
end

def authorization_from(response)
response[:txn_id]
end

def message_from(response)
response[:response_text] || response[:status_description]
end

def expdate(credit_card)
"#{format(credit_card.month, :two_digits)}/#{format(credit_card.year, :two_digits)}"
end

def parse(body)
xml = REXML::Document.new(body)

response = {}

xml.root.elements.to_a.each do |node|
parse_element(response, node)
end

response
end

def parse_element(response, node)
if node.has_elements?
node.elements.each{|element| parse_element(response, element) }
else
response[node.name.underscore.to_sym] = node.text
end
end

# YYYYDDMMHHNNSSKKK000sOOO
def generate_timestamp
time = Time.now.utc
time.strftime("%Y%d%m%H%M%S#{time.usec}+000")
end

end
end
end
5 changes: 5 additions & 0 deletions test/fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ modern_payments:
moneris:
login: store1
password: yesguy

#Working credentials, no need to replace
nab_transact:
login: ABC0001
password: changeit

netaxept:
login: LOGIN
Expand Down
Loading

0 comments on commit 9b4462c

Please sign in to comment.