lib/active_merchant/billing/gateways/psl_card.rb in jelaniharris-activemerchant-1.24.1 vs lib/active_merchant/billing/gateways/psl_card.rb in jelaniharris-activemerchant-1.29.1
- old
+ new
@@ -1,250 +1,249 @@
module ActiveMerchant
module Billing
#
# ActiveMerchant PSL Card Gateway
- #
+ #
# Notes:
# -To be able to use the capture function, the IP address of the machine must be
# registered with PSL
- # -ESALE_KEYED should only be used in situations where the cardholder perceives the
- # transaction to be Internet-based, such as purchasing from a web site/on-line store.
- # If the Internet is used purely for the transport of information from the merchant
- # directly to the gateway then the appropriate cardholder present or not present message
+ # -ESALE_KEYED should only be used in situations where the cardholder perceives the
+ # transaction to be Internet-based, such as purchasing from a web site/on-line store.
+ # If the Internet is used purely for the transport of information from the merchant
+ # directly to the gateway then the appropriate cardholder present or not present message
# type should be used rather than the āEā equivalent.
# -The CV2 / AVS policies are set up with the account settings when signing up for an account
class PslCardGateway < Gateway
self.money_format = :cents
self.default_currency = 'GBP'
-
+
self.supported_countries = ['GB']
# Visa Credit, Visa Debit, Mastercard, Maestro, Solo, Electron,
# American Express, Diners Club, JCB, International Maestro,
# Style, Clydesdale Financial Services, Other
-
+
self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb, :switch, :solo, :maestro ]
self.homepage_url = 'http://www.paymentsolutionsltd.com/'
self.display_name = 'PSL Payment Solutions'
-
+
# Default ISO 3166 country code (GB)
cattr_accessor :location
self.location = 826
-
- # PslCard server URL - The url is the same whether testing or live - use
+
+ # PslCard server self.live_url - The url is the same whether testing or live - use
# the test account when testing...
- URL = 'https://pslcard3.paymentsolutionsltd.com/secure/transact.asp?'
-
+ self.live_url = self.test_url = 'https://pslcard3.paymentsolutionsltd.com/secure/transact.asp?'
+
# eCommerce sale transaction, details keyed by merchant or cardholder
- MESSAGE_TYPE = 'ESALE_KEYED'
-
+ MESSAGE_TYPE = 'ESALE_KEYED'
+
# The type of response that we want to get from PSL, options are HTML, XML or REDIRECT
RESPONSE_ACTION = 'HTML'
-
+
# Currency Codes
CURRENCY_CODES = {
'AUD' => 036,
'GBP' => 826,
'USD' => 840
}
-
+
#The terminal used - only for swipe transactions, so hard coded to 32 for online
EMV_TERMINAL_TYPE = 32
-
+
#Different Dispatch types
DISPATCH_LATER = 'LATER'
DISPATCH_NOW = 'NOW'
-
+
# Return codes
APPROVED = '00'
-
+
#Nominal amount to authorize for a 'dispatch later' type
#The nominal amount is held straight away, when the goods are ready
#to be dispatched, PSL is informed and the full amount is the
#taken.
NOMINAL_AMOUNT = 101
-
+
AVS_CODE = {
"ALL MATCH" => 'Y',
"SECURITY CODE MATCH ONLY" => 'N',
"ADDRESS MATCH ONLY" => 'Y',
"NO DATA MATCHES" => 'N',
"DATA NOT CHECKED" => 'R',
"SECURITY CHECKS NOT SUPPORTED" => 'X'
}
-
+
CVV_CODE = {
"ALL MATCH" => 'M',
"SECURITY CODE MATCH ONLY" => 'M',
"ADDRESS MATCH ONLY" => 'N',
"NO DATA MATCHES" => 'N',
"DATA NOT CHECKED" => 'P',
"SECURITY CHECKS NOT SUPPORTED" => 'X'
}
-
+
# Create a new PslCardGateway
- #
+ #
# The gateway requires that a valid :login be passed in the options hash
- #
+ #
# Paramaters:
# -options:
# :login - the PslCard account login (required)
def initialize(options = {})
requires!(options, :login)
-
- @options = options
+
super
end
# Purchase the item straight away
- #
+ #
# Parameters:
# -money: Amount to be charged as an Integer value in cents
# -authorization: the PSL cross reference from the previous authorization
# -options:
#
# Returns:
# -ActiveRecord::Billing::Response object
- #
+ #
def purchase(money, credit_card, options = {})
post = {}
-
+
add_amount(post, money, DISPATCH_NOW, options)
add_credit_card(post, credit_card)
add_address(post, options)
add_invoice(post, options)
add_purchase_details(post)
-
+
commit(post)
end
-
+
# Authorize the transaction
- #
- # Reserves the funds on the customer's credit card, but does not
+ #
+ # Reserves the funds on the customer's credit card, but does not
# charge the card.
#
# This implementation does not authorize the full amount, rather it checks that the full amount
# is available and only 'reserves' the nominal amount (currently a pound and a penny)
- #
+ #
# Parameters:
# -money: Amount to be charged as an Integer value in cents
# -authorization: the PSL cross reference from the previous authorization
# -options:
#
# Returns:
# -ActiveRecord::Billing::Response object
- #
+ #
def authorize(money, credit_card, options = {})
post = {}
-
+
add_amount(post, money, DISPATCH_LATER, options)
add_credit_card(post, credit_card)
add_address(post, options)
add_invoice(post, options)
add_purchase_details(post)
-
+
commit(post)
end
-
- # Post an authorization.
+
+ # Post an authorization.
#
- # Captures the funds from an authorized transaction.
- #
+ # Captures the funds from an authorized transaction.
+ #
# Parameters:
# -money: Amount to be charged as an Integer value in cents
# -authorization: The PSL Cross Reference
# -options:
#
# Returns:
# -ActiveRecord::Billing::Response object
#
def capture(money, authorization, options = {})
post = {}
-
+
add_amount(post, money, DISPATCH_NOW, options)
add_reference(post, authorization)
add_purchase_details(post)
commit(post)
end
private
-
+
def add_credit_card(post, credit_card)
post[:QAName] = credit_card.name
post[:CardNumber] = credit_card.number
post[:EMVTerminalType] = EMV_TERMINAL_TYPE
post[:ExpMonth] = credit_card.month
post[:ExpYear] = credit_card.year
-
- if requires_start_date_or_issue_number?(credit_card)
+
+ if requires_start_date_or_issue_number?(credit_card)
post[:IssueNumber] = credit_card.issue_number unless credit_card.issue_number.blank?
post[:StartMonth] = credit_card.start_month unless credit_card.start_month.blank?
post[:StartYear] = credit_card.start_year unless credit_card.start_year.blank?
end
-
+
# CV2 check
post[:AVSCV2Check] = credit_card.verification_value? ? 'YES' : 'NO'
post[:CV2] = credit_card.verification_value if credit_card.verification_value?
end
-
+
def add_address(post, options)
address = options[:billing_address] || options[:address]
return if address.nil?
-
+
post[:QAAddress] = [:address1, :address2, :city, :state].collect{|a| address[a]}.reject{|a| a.blank?}.join(' ')
post[:QAPostcode] = address[:zip]
end
-
+
def add_invoice(post, options)
post[:MerchantName] = options[:merchant] || 'Merchant Name' # May use this as the order_id field
post[:OrderID] = options[:order_id] unless options[:order_id].blank?
end
-
+
def add_reference(post, authorization)
post[:CrossReference] = authorization
end
-
+
def add_amount(post, money, dispatch_type, options)
post[:CurrencyCode] = currency_code(options[:currency] || currency(money))
-
+
if dispatch_type == DISPATCH_LATER
post[:amount] = amount(NOMINAL_AMOUNT)
post[:DispatchLaterAmount] = amount(money)
else
post[:amount] = amount(money)
end
-
+
post[:Dispatch] = dispatch_type
end
-
+
def add_purchase_details(post)
post[:EchoAmount] = 'YES'
post[:SCBI] = 'YES' # Return information about the transaction
post[:MessageType] = MESSAGE_TYPE
end
-
+
# Get the currency code for the passed money object
- #
+ #
# The money class stores the currency as an ISO 4217:2001 Alphanumeric,
# however PSL requires the ISO 4217:2001 Numeric code.
- #
+ #
# Parameters:
# -money: Integer value in cents
- #
+ #
# Returns:
# -the ISO 4217:2001 Numberic currency code
- #
+ #
def currency_code(currency)
CURRENCY_CODES[currency]
end
-
+
# Parse the PSL response and create a Response object
#
# Parameters:
# -body: The response string returned from PSL, Formatted:
# Key=value&key=value...
- #
+ #
# Returns:
# -a hash with all of the values returned in the PSL response
#
def parse(body)
@@ -253,38 +252,38 @@
key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
fields[key] = CGI.unescape(value)
end
fields.symbolize_keys
end
-
+
# Send the passed data to PSL for processing
#
# Parameters:
# -request: The data that is to be sent to PSL
#
# Returns:
# - ActiveMerchant::Billing::Response object
#
def commit(request)
- response = parse( ssl_post(URL, post_data(request)) )
-
- Response.new(response[:ResponseCode] == APPROVED, response[:Message], response,
- :test => test?,
+ response = parse( ssl_post(self.live_url, post_data(request)) )
+
+ Response.new(response[:ResponseCode] == APPROVED, response[:Message], response,
+ :test => test?,
:authorization => response[:CrossReference],
:cvv_result => CVV_CODE[response[:AVSCV2Check]],
:avs_result => { :code => AVS_CODE[response[:AVSCV2Check]] }
)
end
-
+
# Put the passed data into a format that can be submitted to PSL
# Key=Value&Key=Value...
#
# Any ampersands and equal signs are removed from the data being posted
- # as PSL puts them back into the response string which then cannot be parsed.
+ # as PSL puts them back into the response string which then cannot be parsed.
# This is after escaping before sending the request to PSL - this is a work
# around for the time being
- #
+ #
# Parameters:
# -post: Hash of all the data to be sent
#
# Returns:
# -String: the data to be sent
@@ -292,10 +291,10 @@
def post_data(post)
post[:CountryCode] = self.location
post[:MerchantID] = @options[:login]
post[:ValidityID] = @options[:password]
post[:ResponseAction] = RESPONSE_ACTION
-
+
post.collect { |key, value|
"#{key}=#{CGI.escape(value.to_s.tr('&=', ' '))}"
}.join("&")
end
end