require 'nokogiri'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class BpointGateway < Gateway
self.live_url = 'https://www.bpoint.com.au/evolve/service_1_4_4.asmx'
self.supported_countries = ['AU']
self.default_currency = 'AUD'
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
self.homepage_url = 'https://www.bpoint.com.au/bpoint'
self.display_name = 'BPoint'
def initialize(options={})
requires!(options, :username, :password, :merchant_number)
super
end
def store(credit_card, options={})
options[:crn1] ||= 'DEFAULT'
request_body = soap_request do |xml|
add_token(xml, credit_card, options)
end
commit(request_body)
end
def purchase(amount, credit_card, options={})
request_body = soap_request do |xml|
process_payment(xml) do |payment_xml|
add_purchase(payment_xml, amount, credit_card, options)
end
end
commit(request_body)
end
def authorize(amount, credit_card, options={})
request_body = soap_request do |xml|
process_payment(xml) do |payment_xml|
add_authorize(payment_xml, amount, credit_card, options)
end
end
commit(request_body)
end
def capture(amount, authorization, options={})
request_body = soap_request do |xml|
process_payment(xml) do |payment_xml|
add_capture(payment_xml, amount, authorization, options)
end
end
commit(request_body)
end
def refund(amount, authorization, options={})
request_body = soap_request do |xml|
process_payment(xml) do |payment_xml|
add_refund(payment_xml, amount, authorization, options)
end
end
commit(request_body)
end
def void(amount, authorization, options={})
request_body = soap_request do |xml|
process_payment(xml) do |payment_xml|
add_void(payment_xml, amount, authorization, options)
end
end
commit(request_body)
end
def verify(credit_card, options={})
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, credit_card, options) }
r.process(:ignore_result) { void(100, r.authorization, options) }
end
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2').
gsub(%r(().+()), '\1[FILTERED]\2')
end
private
def soap_request
Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
xml.send('soap12:Envelope', soap_envelope_attributes) {
xml.send('soap12:Body') {
yield(xml) if block_given?
}
}
end.to_xml
end
def soap_envelope_attributes
{ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
'xmlns:soap12' => 'http://www.w3.org/2003/05/soap-envelope' }
end
def process_payment(xml)
xml.send('ProcessPayment', { 'xmlns' => 'urn:Eve_1_4_4' }) {
credentials_xml(xml)
xml.send('txnReq') {
yield(xml) if block_given?
}
}
end
def add_token(xml, credit_card, options)
xml.send('AddToken', { 'xmlns' => 'urn:Eve_1_4_4' }) {
credentials_xml(xml)
xml.send('tokenRequest') {
xml.send('CRN1', options[:crn1])
xml.send('CRN2', '')
xml.send('CRN3', '')
xml.send('CardNumber', credit_card.number)
xml.send('ExpiryDate', expdate(credit_card))
}
}
end
def credentials_xml(xml)
xml.send('username', @options[:username])
xml.send('password', @options[:password])
xml.send('merchantNumber', @options[:merchant_number])
end
def add_purchase(xml, amount, credit_card, options)
payment_xml(xml, 'PAYMENT', amount, options)
credit_card_xml(xml, credit_card)
end
def add_authorize(xml, amount, credit_card, options)
payment_xml(xml, 'PREAUTH', amount, options)
credit_card_xml(xml, credit_card)
end
def add_capture(xml, amount, transaction_number, options)
payment_xml(xml, 'CAPTURE', amount, options)
transaction_number_xml(xml, transaction_number)
end
def add_refund(xml, amount, transaction_number, options)
payment_xml(xml, 'REFUND', amount, options)
transaction_number_xml(xml, transaction_number)
end
def add_void(xml, amount, transaction_number, options)
payment_xml(xml, 'REVERSAL', amount, options)
transaction_number_xml(xml, transaction_number)
end
def payment_xml(xml, payment_type, amount, options)
xml.send('PaymentType', payment_type)
xml.send('TxnType', 'WEB_SHOP')
xml.send('BillerCode', options.fetch(:biller_code, ''))
xml.send('MerchantReference', options[:order_id]) if options[:order_id]
xml.send('CRN1', options[:crn1]) if options[:crn1]
xml.send('CRN2', options[:crn2]) if options[:crn2]
xml.send('CRN3', options[:crn3]) if options[:crn3]
xml.send('Amount', amount)
end
def credit_card_xml(xml, credit_card)
xml.send('CardNumber', credit_card.number)
xml.send('ExpiryDate', expdate(credit_card))
xml.send('CVC', credit_card.verification_value)
end
def transaction_number_xml(xml, transaction_number)
xml.send('OriginalTransactionNumber', transaction_number)
end
def commit(request_body)
parse(ssl_post(live_url, request_body, request_headers))
end
def request_headers
{ 'Content-Type' => 'application/soap+xml; charset=utf-8' }
end
def parse(body)
response_for(Nokogiri::XML(body).remove_namespaces!)
end
def response_for(xml_doc)
if xml_doc.xpath('//ProcessPaymentResponse').any?
ProcessPaymentResponse.new(xml_doc, self).to_response
elsif xml_doc.xpath('//AddTokenResponse').any?
AddTokenResponse.new(xml_doc, self).to_response
end
end
class BPointResponse
attr_reader :xml_doc, :gateway, :params
def initialize(xml_doc, gateway)
@xml_doc = xml_doc
@gateway = gateway
@params = init_params
end
def to_response
Response.new(success?, message, params, options)
end
private
def init_params
{}.tap do |h|
xml_doc.xpath(response_node).each do |node|
if node.elements.empty?
h[node.name.to_sym] = node.text
else
node.elements.each do |childnode|
name = "#{node.name}_#{childnode.name}"
h[name.to_sym] = childnode.text
end
end
end
end
end
def response_node
"//#{self.class.name.split('::').last}/*"
end
def options
{ authorization: params[authorization_key], test: gateway.test? }
end
end
class ProcessPaymentResponse < BPointResponse
private
def authorization_key
:ProcessPaymentResult_TransactionNumber
end
def success?
params[:ProcessPaymentResult_ResponseCode] == '0'
end
def message
params[:ProcessPaymentResult_AuthorisationResult] || params[:response_ResponseMessage]
end
end
class AddTokenResponse < BPointResponse
private
def authorization_key
:AddTokenResult_Token
end
def success?
params[:response_ResponseCode] == 'SUCCESS'
end
def message
params[:response_ResponseCode].capitalize
end
end
end
end
end