forked from activemerchant/active_merchant
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Gateway] Add new NABTransact(AU) Gateway
- Loading branch information
Showing
5 changed files
with
538 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.