Skip to content

Instantly share code, notes, and snippets.

@nickcoyne
Created October 16, 2009 05:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nickcoyne/211597 to your computer and use it in GitHub Desktop.
Save nickcoyne/211597 to your computer and use it in GitHub Desktop.
Paygate gateway for ActiveMerchant
require 'rexml/document'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
# Paygate is one of the leading CC gateways in South Africa
# see http://www.paygate.co.za
# Contributed by Nick Coyne (http://www.codevader.com)
class PaygateGateway < Gateway
URL = 'https://www.paygate.co.za/payxml/process.trans'
API_VERSION = '4.0'
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
self.supported_countries = ['ZA']
self.default_currency = 'ZAR'
self.money_format = :cents
self.homepage_url = 'http://www.paygate.co.za'
self.display_name = 'Paygate'
@@credit_card_codes = {
:visa => '1',
:master => '2',
:american_express => '3',
:diners_club => '4'
}
TRANSACTIONS = {
:authorization => 'auth',
:purchase => 'auth', # this is the default account behaviour for Paygate. To do a 2-step auth/capture your Paygate account must have that option enabled.
:capture => 'settle',
:status => 'query',
:credit => 'refund'
}
SUCCESS_TYPES = ['1', '3', '4', '5']
# login is your Paygate customer id
# password is your Paygate password
# auto_settle is your paygate account auto-settlement option. Auto-settle is on by default, so set this to true.
def initialize(options = {})
requires!(options, :login, :password, :auto_settle)
@options = options
super
end
def authorize(money, creditcard, options = {})
raise StandardError, "Authorize method only available if auto-settle is disabled on your Paygate account" if @options[:auto_settle] == true
post = {}
add_amount(post, money, options)
add_invoice(post, options)
add_creditcard(post, creditcard)
add_customer_data(post, options)
commit(:authorization, post)
end
def capture(options = {})
raise StandardError, "Capture method only available if auto-settle is disabled on your Paygate account" if @options[:auto_settle] == true
requires!(options, :authorization)
post = {}
post[:tid] = options[:authorization]
post[:bno] = options[:batch_no] unless options[:batch_no].blank?
commit(:capture, post)
end
def purchase(money, creditcard, options = {})
raise StandardError, "Purchase method only available if auto-settle is enabled on your Paygate account" if @options[:auto_settle] == false
post = {}
add_amount(post, money, options)
add_invoice(post, options)
add_creditcard(post, creditcard)
add_customer_data(post, options)
commit(:purchase, post)
end
def credit(money, identification, options = {})
post = {}
post[:amt] = amount(money)
post[:tid] = identification
post[:bno] = options[:batch_no] unless options[:batch_no].blank?
commit(:credit, post)
end
def status(identification)
post = {}
post[:tid] = identification
commit(:status, post)
end
private
def add_amount(post, money, options = {})
post[:amt] = amount(money)
post[:cur] = options[:currency] || currency(money)
end
def add_invoice(post, options)
post[:cref] = options[:order_id]
post[:bno] = options[:batch_no]
end
def add_creditcard(post, creditcard)
post[:cc] = creditcard.number
post[:cvv] = creditcard.verification_value
post[:exp] = expdate(creditcard)
post[:cname] = "#{ creditcard.first_name } #{ creditcard.last_name }"
post[:budp] = '0'
end
def expdate(credit_card)
month = sprintf("%.2i", credit_card.month)
"#{month}#{credit_card.year}"
end
def add_customer_data(post, options)
post[:ip] = options[:ip] unless options[:ip].blank?
post[:email] = options[:email] unless options[:email].blank?
end
def parse(xml)
response = {}
xml = REXML::Document.new(xml)
xml.elements['protocol/*rx'].attributes.each {|a,v| response[a.to_sym] = v.to_s }
response
end
def commit(action, params)
response = parse(ssl_post(URL, post_data(action, params)))
Response.new(successful?(response), message_from(response), response,
:authorization => response[:tid],
:risk => response[:risk]
)
end
def successful?(response)
SUCCESS_TYPES.include?(response[:stat])
end
def authorization_from(response)
[ response[:authnum], response[:transid] ].compact.join(";")
end
def message_from(response)
response[:rdesc] || "#{ response[:ecode] }: #{ response[:edesc] }"
end
def post_data(action, parameters = {})
xml = REXML::Document.new
root = xml.add_element("protocol")
root.attributes["ver"] = API_VERSION
root.attributes["pgid"] = options[:login]
root.attributes["pwd"] = options[:password]
transaction = root.add_element("#{TRANSACTIONS[action].to_s}tx")
parameters.each do |key, value|
transaction.attributes[key.to_s] = value.to_s unless key.blank?
end
xml.to_s
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment