Skip to content

Commit

Permalink
Add IPP gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
InfraRuby authored and ntalbott committed Dec 19, 2014
1 parent 50d18da commit f3feffc
Show file tree
Hide file tree
Showing 7 changed files with 481 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* eWay Rapid: Update supported countries and card types [incarnate]
* PayPal: Allow specifying ButtonSource at init [ntalbott]
* Payflow: Add fraud_review support [ntalbott]
* Add IPP gateway [InfraRuby]

== Version 1.45.0 (December 1, 2014)

Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -509,3 +509,7 @@ Bank Frick (September 2014)
Global Transport (September 2014)

* Duff O'Melia (duff)

IPP (December 2014)

* (InfraRuby)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
* [iATS Payments](http://home.iatspayments.com/) - AU, BR, CA, CH, DE, DK, ES, FI, FR, GR, HK, IE, IT, NL, NO, PT, SE, SG, TR, UK, US
* [Inspire Commerce](http://www.inspiregateway.com) - US
* [InstaPay](http://www.instapayllc.com) - US
* [IPP](http://www.ippayments.com.au/) - AU
* [Iridium](http://www.iridiumcorp.co.uk/) - GB, ES
* [iTransact](http://www.itransact.com/) - US
* [JetPay](http://www.jetpay.com/) - US
Expand Down
175 changes: 175 additions & 0 deletions lib/active_merchant/billing/gateways/ipp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
require "nokogiri"

module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class IppGateway < Gateway
self.live_url = 'https://www.ippayments.com.au/interface/api/dts.asmx'
self.test_url = 'https://demo.ippayments.com.au/interface/api/dts.asmx'

self.supported_countries = ['AU']
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]

self.homepage_url = 'http://www.ippayments.com.au/'
self.display_name = 'IPP'

self.money_format = :cents

STANDARD_ERROR_CODE_MAPPING = {
"05" => STANDARD_ERROR_CODE[:card_declined],
"06" => STANDARD_ERROR_CODE[:processing_error],
"14" => STANDARD_ERROR_CODE[:invalid_number],
"54" => STANDARD_ERROR_CODE[:expired_card],
}

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

def purchase(money, payment, options={})
commit("SubmitSinglePayment") do |xml|
xml.Transaction do
xml.CustRef options[:order_id]
add_amount(xml, money)
xml.TrnType "1"
add_credit_card(xml, payment)
add_credentials(xml)
xml.TrnSource options[:ip]
end
end
end

def authorize(money, payment, options={})
commit("SubmitSinglePayment") do |xml|
xml.Transaction do
xml.CustRef options[:order_id]
add_amount(xml, money)
xml.TrnType "2"
add_credit_card(xml, payment)
add_credentials(xml)
xml.TrnSource options[:ip]
end
end
end

def capture(money, authorization, options={})
commit("SubmitSingleCapture") do |xml|
xml.Capture do
xml.Receipt authorization
add_amount(xml, money)
add_credentials(xml)
end
end
end

def refund(money, authorization, options={})
commit("SubmitSingleRefund") do |xml|
xml.Refund do
xml.Receipt authorization
add_amount(xml, money)
add_credentials(xml)
end
end
end

def supports_scrubbing?
true
end

def scrub(transcript)
transcript.
gsub(%r((<CardNumber>)[^<]+(<))i, '\1[FILTERED]\2').
gsub(%r((<CVN>)[^<]+(<))i, '\1[FILTERED]\2').
gsub(%r((<Password>)[^<]+(<))i, '\1[FILTERED]\2')
end

private

def add_credentials(xml)
xml.Security do
xml.UserName @options[:username]
xml.Password @options[:password]
end
end

def add_amount(xml, money)
xml.Amount amount(money)
end

def add_credit_card(xml, payment)
xml.CreditCard :Registered => "False" do
xml.CardNumber payment.number
xml.ExpM format(payment.month, :two_digits)
xml.ExpY format(payment.year, :four_digits)
xml.CVN payment.verification_value
xml.CardHolderName payment.name
end
end

def parse(body)
element = Nokogiri::XML(body).root.first_element_child.first_element_child

response = {}
doc = Nokogiri::XML(element)
doc.root.elements.each do |e|
response[e.name.underscore.to_sym] = e.inner_text
end
response
end

def commit(action, &block)
headers = {
"Content-Type" => "text/xml; charset=utf-8",
"SOAPAction" => "http://www.ippayments.com.au/interface/api/dts/#{action}",
}
response = parse(ssl_post(commit_url, new_submit_xml(action, &block), headers))

Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
error_code: error_code_from(response),
test: test?,
)
end

def new_submit_xml(action)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
xml.soap :Envelope, "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema", "xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/" do
xml.soap :Body do
xml.__send__(action, "xmlns" => "http://www.ippayments.com.au/interface/api/dts") do
xml.trnXML do
inner_xml = Builder::XmlMarkup.new(indent: 2)
yield(inner_xml)
xml.cdata!(inner_xml.target!)
end
end
end
end
xml.target!
end

def commit_url
(test? ? test_url : live_url)
end

def success_from(response)
(response[:response_code] == "0")
end

def error_code_from(response)
STANDARD_ERROR_CODE_MAPPING[response[:declined_code]]
end

def message_from(response)
response[:declined_message]
end

def authorization_from(response)
response[:receipt]
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 @@ -258,6 +258,11 @@ instapay:
login: TEST0
password:

# Working credentials, no need to replace
ipp:
username: nmi.api
password: qwerty123

iridium:
login: LOGIN
password: PASSWORD
Expand Down
84 changes: 84 additions & 0 deletions test/remote/gateways/remote_ipp_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require 'test_helper'

class RemoteIppTest < Test::Unit::TestCase
def setup
@gateway = IppGateway.new(fixtures(:ipp))

@credit_card = credit_card('4005550000000001')

@options = {
order_id: '1',
billing_address: address,
description: 'Store Purchase',
}
end

def test_dump_transcript
skip("Transcript scrubbing for this gateway has been tested.")
dump_transcript_and_fail(@gateway, @amount, @credit_card, @options)
end

def test_transcript_scrubbing
transcript = capture_transcript(@gateway) do |gateway|
gateway.purchase(@amount, @credit_card, @options)
end
transcript = @gateway.scrub(transcript)

assert_scrubbed(@credit_card.number, transcript)
assert_scrubbed(@credit_card.verification_value, transcript)
assert_scrubbed(@gateway.options[:password], transcript)
end

def test_successful_purchase
response = @gateway.purchase(200, @credit_card, @options)
assert_success response
assert_equal '', response.message
end

def test_failed_purchase
response = @gateway.purchase(105, @credit_card, @options)
assert_failure response
assert_equal 'Do Not Honour', response.message
assert_equal Gateway::STANDARD_ERROR_CODE[:card_declined], response.error_code
end

def test_successful_authorize_and_capture
response = @gateway.authorize(200, @credit_card, @options)
assert_success response
response = @gateway.capture(200, response.authorization)
assert_success response
end

def test_failed_authorize
response = @gateway.authorize(105, @credit_card, @options)
assert_failure response
end

def test_failed_capture
response = @gateway.capture(200, '')
assert_failure response
end

def test_successful_refund
response = @gateway.purchase(200, @credit_card, @options)
response = @gateway.refund(200, response.authorization, @options)
assert_success response
assert_equal '', response.message
end

def test_failed_refund
response = @gateway.purchase(200, @credit_card, @options)
response = @gateway.refund(105, response.authorization, @options)
assert_failure response
assert_equal 'Do Not Honour', response.message
end

def test_invalid_login
gateway = IppGateway.new(
username: '',
password: '',
)
response = gateway.purchase(200, @credit_card, @options)
assert_failure response
end
end
Loading

0 comments on commit f3feffc

Please sign in to comment.