mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-18 21:56:55 -04:00
Vendored the activemerchant gem to help with testing
We needed a bogus paypal gateway as per http://infotrope.net/2013/05/31/testing-paypal-express-with-activemerchants-bogusgateway-and-how-to-make-it-work/ Tests now pass usefully.
This commit is contained in:
9
Gemfile
9
Gemfile
@@ -9,7 +9,14 @@ gem 'haml'
|
||||
|
||||
gem 'cancan'
|
||||
|
||||
gem 'activemerchant'
|
||||
# vendored activemerchant for testing- needed for bogus paypal
|
||||
# gateway monkeypatch
|
||||
gem 'activemerchant', '1.33.0',
|
||||
:path => 'vendor/gems/activemerchant-1.33.0',
|
||||
:require => 'active_merchant'
|
||||
gem 'active_utils', '1.0.5',
|
||||
:path => 'vendor/gems/active_utils-1.0.5'
|
||||
|
||||
|
||||
# Bundle edge Rails instead:
|
||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||
|
||||
28
Gemfile.lock
28
Gemfile.lock
@@ -1,3 +1,15 @@
|
||||
PATH
|
||||
remote: vendor/gems/active_utils-1.0.5
|
||||
specs:
|
||||
active_utils (1.0.5)
|
||||
activesupport (>= 2.3.11)
|
||||
i18n
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/activemerchant-1.33.0
|
||||
specs:
|
||||
activemerchant (1.33.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
@@ -14,17 +26,6 @@ GEM
|
||||
rack-cache (~> 1.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.2.1)
|
||||
active_utils (1.0.5)
|
||||
activesupport (>= 2.3.11)
|
||||
i18n
|
||||
activemerchant (1.32.1)
|
||||
active_utils (>= 1.0.2)
|
||||
activesupport (>= 2.3.14)
|
||||
builder (>= 2.0.0)
|
||||
i18n
|
||||
json (>= 1.5.1)
|
||||
money
|
||||
nokogiri
|
||||
activemodel (3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
builder (~> 3.0.0)
|
||||
@@ -127,8 +128,6 @@ GEM
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
mime-types (1.21)
|
||||
money (5.1.1)
|
||||
i18n (~> 0.6.0)
|
||||
multi_json (1.7.1)
|
||||
net-scp (1.1.0)
|
||||
net-ssh (>= 2.6.5)
|
||||
@@ -245,7 +244,8 @@ PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activemerchant
|
||||
active_utils (= 1.0.5)!
|
||||
activemerchant (= 1.33.0)!
|
||||
bluecloth
|
||||
bootstrap-datepicker-rails
|
||||
bundler (>= 1.1.5)
|
||||
|
||||
@@ -51,7 +51,12 @@ class OrdersController < ApplicationController
|
||||
@order.completed_at = Time.zone.now
|
||||
@order.save
|
||||
|
||||
@order.record_paypal_details(params[:token])
|
||||
if (params[:token])
|
||||
@order.record_paypal_details(params[:token])
|
||||
else
|
||||
flash[:alert] = "PayPal didn't return a token for your order. Please notify support."
|
||||
end
|
||||
|
||||
@order.update_account # apply paid account benefits, etc.
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
@@ -48,8 +48,8 @@ Growstuff::Application.configure do
|
||||
|
||||
config.after_initialize do
|
||||
ActiveMerchant::Billing::Base.mode = :test
|
||||
::STANDARD_GATEWAY = ActiveMerchant::Billing::BogusGateway.new
|
||||
::EXPRESS_GATEWAY = ActiveMerchant::Billing::BogusGateway.new
|
||||
::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalBogusGateway.new
|
||||
::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalBogusGateway.new
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -32,6 +32,17 @@ describe OrdersController do
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET checkout" do
|
||||
it "redirects to Paypal" do
|
||||
member = FactoryGirl.create(:member)
|
||||
sign_in member
|
||||
order = Order.create!(:member_id => member.id)
|
||||
get :checkout, {:id => order.to_param}
|
||||
response.status.should eq 302
|
||||
response.redirect_url.should match /paypal\.com/
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET complete" do
|
||||
it "assigns the requested order as @order" do
|
||||
member = FactoryGirl.create(:member)
|
||||
|
||||
5
vendor/gems/active_utils-1.0.5/.gitignore
vendored
Normal file
5
vendor/gems/active_utils-1.0.5/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
pkg/*
|
||||
*.gem
|
||||
.bundle
|
||||
.DS_Store
|
||||
Gemfile.lock
|
||||
3
vendor/gems/active_utils-1.0.5/Gemfile
vendored
Normal file
3
vendor/gems/active_utils-1.0.5/Gemfile
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gemspec
|
||||
20
vendor/gems/active_utils-1.0.5/MIT-LICENSE
vendored
Normal file
20
vendor/gems/active_utils-1.0.5/MIT-LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2011 Shopify
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
15
vendor/gems/active_utils-1.0.5/README.md
vendored
Normal file
15
vendor/gems/active_utils-1.0.5/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Active Utils
|
||||
|
||||
Active Utils extracts commonly used modules and classes used by [Active Merchant](http://github.com/Shopify/active_merchant), [Active Shipping](http://github.com/Shopify/active_shipping), and [Active Fulfillment](http://github.com/Shopify/active_fulfillment).
|
||||
|
||||
### Includes
|
||||
|
||||
* Connection - base class for making HTTP requests
|
||||
* Country - find countries mapped by name, country codes, and numeric values
|
||||
* Error - common error classes used throughout the Active projects
|
||||
* PostData - helper class for managing required fields that are to be POST-ed
|
||||
* PostsData - making SSL HTTP requests
|
||||
* RequiresParameters - helper method to ensure the required parameters are passed in
|
||||
* Utils - common utils such as uid generator
|
||||
* Validateable - module used for making models validateable
|
||||
* NetworkConnectionRetries - module for retrying network connections when connection errors occur
|
||||
13
vendor/gems/active_utils-1.0.5/Rakefile
vendored
Normal file
13
vendor/gems/active_utils-1.0.5/Rakefile
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
require 'bundler'
|
||||
Bundler::GemHelper.install_tasks
|
||||
|
||||
require 'rake/testtask'
|
||||
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.pattern = 'test/unit/**/*_test.rb'
|
||||
t.ruby_opts << '-rubygems'
|
||||
t.libs << 'test'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
task :default => "test"
|
||||
26
vendor/gems/active_utils-1.0.5/active_utils.gemspec
vendored
Normal file
26
vendor/gems/active_utils-1.0.5/active_utils.gemspec
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "active_utils/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "active_utils"
|
||||
s.version = ActiveUtils::VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["Shopify"]
|
||||
s.email = ["developers@jadedpixel.com"]
|
||||
s.homepage = "http://github.com/shopify/active_utils"
|
||||
s.summary = %q{Common utils used by active_merchant, active_fulfillment, and active_shipping}
|
||||
|
||||
s.rubyforge_project = "active_utils"
|
||||
|
||||
s.add_dependency('activesupport', '>= 2.3.11')
|
||||
s.add_dependency('i18n')
|
||||
|
||||
s.add_development_dependency('rake')
|
||||
s.add_development_dependency('mocha')
|
||||
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
s.require_paths = ["lib"]
|
||||
end
|
||||
20
vendor/gems/active_utils-1.0.5/lib/active_utils.rb
vendored
Normal file
20
vendor/gems/active_utils-1.0.5/lib/active_utils.rb
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
require 'active_support/core_ext/hash/indifferent_access'
|
||||
require 'active_support/core_ext/hash/conversions'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActiveMerchant
|
||||
autoload :NetworkConnectionRetries, 'active_utils/common/network_connection_retries'
|
||||
autoload :Connection, 'active_utils/common/connection'
|
||||
autoload :Country, 'active_utils/common/country'
|
||||
autoload :CountryCode, 'active_utils/common/country'
|
||||
autoload :ActiveMerchantError, 'active_utils/common/error'
|
||||
autoload :ConnectionError, 'active_utils/common/error'
|
||||
autoload :RetriableConnectionError, 'active_utils/common/error'
|
||||
autoload :ResponseError, 'active_utils/common/error'
|
||||
autoload :ClientCertificateError, 'active_utils/common/error'
|
||||
autoload :PostData, 'active_utils/common/post_data'
|
||||
autoload :PostsData, 'active_utils/common/posts_data'
|
||||
autoload :RequiresParameters, 'active_utils/common/requires_parameters'
|
||||
autoload :Utils, 'active_utils/common/utils'
|
||||
autoload :Validateable, 'active_utils/common/validateable'
|
||||
end
|
||||
147
vendor/gems/active_utils-1.0.5/lib/active_utils/common/connection.rb
vendored
Normal file
147
vendor/gems/active_utils-1.0.5/lib/active_utils/common/connection.rb
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
require 'uri'
|
||||
require 'net/http'
|
||||
require 'net/https'
|
||||
require 'benchmark'
|
||||
|
||||
module ActiveMerchant
|
||||
class Connection
|
||||
include NetworkConnectionRetries
|
||||
|
||||
MAX_RETRIES = 3
|
||||
OPEN_TIMEOUT = 60
|
||||
READ_TIMEOUT = 60
|
||||
VERIFY_PEER = true
|
||||
RETRY_SAFE = false
|
||||
RUBY_184_POST_HEADERS = { "Content-Type" => "application/x-www-form-urlencoded" }
|
||||
|
||||
attr_accessor :endpoint
|
||||
attr_accessor :open_timeout
|
||||
attr_accessor :read_timeout
|
||||
attr_accessor :verify_peer
|
||||
attr_accessor :pem
|
||||
attr_accessor :pem_password
|
||||
attr_accessor :wiredump_device
|
||||
attr_accessor :logger
|
||||
attr_accessor :tag
|
||||
attr_accessor :ignore_http_status
|
||||
|
||||
def initialize(endpoint)
|
||||
@endpoint = endpoint.is_a?(URI) ? endpoint : URI.parse(endpoint)
|
||||
@open_timeout = OPEN_TIMEOUT
|
||||
@read_timeout = READ_TIMEOUT
|
||||
@retry_safe = RETRY_SAFE
|
||||
@verify_peer = VERIFY_PEER
|
||||
@ignore_http_status = false
|
||||
end
|
||||
|
||||
def request(method, body, headers = {})
|
||||
retry_exceptions(:max_retries => MAX_RETRIES, :logger => logger, :tag => tag) do
|
||||
begin
|
||||
info "#{method.to_s.upcase} #{endpoint}", tag
|
||||
|
||||
result = nil
|
||||
|
||||
realtime = Benchmark.realtime do
|
||||
result = case method
|
||||
when :get
|
||||
raise ArgumentError, "GET requests do not support a request body" if body
|
||||
http.get(endpoint.request_uri, headers)
|
||||
when :post
|
||||
debug body
|
||||
http.post(endpoint.request_uri, body, RUBY_184_POST_HEADERS.merge(headers))
|
||||
when :put
|
||||
debug body
|
||||
http.put(endpoint.request_uri, body, headers)
|
||||
when :delete
|
||||
# It's kind of ambiguous whether the RFC allows bodies
|
||||
# for DELETE requests. But Net::HTTP's delete method
|
||||
# very unambiguously does not.
|
||||
raise ArgumentError, "DELETE requests do not support a request body" if body
|
||||
http.delete(endpoint.request_uri, headers)
|
||||
else
|
||||
raise ArgumentError, "Unsupported request method #{method.to_s.upcase}"
|
||||
end
|
||||
end
|
||||
|
||||
info "--> %d %s (%d %.4fs)" % [result.code, result.message, result.body ? result.body.length : 0, realtime], tag
|
||||
debug result.body
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def http
|
||||
http = Net::HTTP.new(endpoint.host, endpoint.port)
|
||||
configure_debugging(http)
|
||||
configure_timeouts(http)
|
||||
configure_ssl(http)
|
||||
configure_cert(http)
|
||||
http
|
||||
end
|
||||
|
||||
def configure_debugging(http)
|
||||
http.set_debug_output(wiredump_device)
|
||||
end
|
||||
|
||||
def configure_timeouts(http)
|
||||
http.open_timeout = open_timeout
|
||||
http.read_timeout = read_timeout
|
||||
end
|
||||
|
||||
def configure_ssl(http)
|
||||
return unless endpoint.scheme == "https"
|
||||
|
||||
http.use_ssl = true
|
||||
|
||||
if verify_peer
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
http.ca_file = File.dirname(__FILE__) + '/../../certs/cacert.pem'
|
||||
else
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
end
|
||||
end
|
||||
|
||||
def configure_cert(http)
|
||||
return if pem.blank?
|
||||
|
||||
http.cert = OpenSSL::X509::Certificate.new(pem)
|
||||
|
||||
if pem_password
|
||||
http.key = OpenSSL::PKey::RSA.new(pem, pem_password)
|
||||
else
|
||||
http.key = OpenSSL::PKey::RSA.new(pem)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_response(response)
|
||||
if @ignore_http_status then
|
||||
return response.body
|
||||
else
|
||||
case response.code.to_i
|
||||
when 200...300
|
||||
response.body
|
||||
else
|
||||
raise ResponseError.new(response)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def debug(message, tag = nil)
|
||||
log(:debug, message, tag)
|
||||
end
|
||||
|
||||
def info(message, tag = nil)
|
||||
log(:info, message, tag)
|
||||
end
|
||||
|
||||
def error(message, tag = nil)
|
||||
log(:error, message, tag)
|
||||
end
|
||||
|
||||
def log(level, message, tag)
|
||||
message = "[#{tag}] #{message}" if tag
|
||||
logger.send(level, message) if logger
|
||||
end
|
||||
end
|
||||
end
|
||||
328
vendor/gems/active_utils-1.0.5/lib/active_utils/common/country.rb
vendored
Normal file
328
vendor/gems/active_utils-1.0.5/lib/active_utils/common/country.rb
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
#!ruby19
|
||||
# encoding: utf-8
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
class InvalidCountryCodeError < StandardError
|
||||
end
|
||||
|
||||
class CountryCodeFormatError < StandardError
|
||||
end
|
||||
|
||||
class CountryCode
|
||||
attr_reader :value, :format
|
||||
def initialize(value)
|
||||
@value = value.to_s.upcase
|
||||
detect_format
|
||||
end
|
||||
|
||||
def to_s
|
||||
value
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def detect_format
|
||||
case @value
|
||||
when /^[[:alpha:]]{2}$/
|
||||
@format = :alpha2
|
||||
when /^[[:alpha:]]{3}$/
|
||||
@format = :alpha3
|
||||
when /^[[:digit:]]{3}$/
|
||||
@format = :numeric
|
||||
else
|
||||
raise CountryCodeFormatError, "The country code is not formatted correctly #{@value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Country
|
||||
include RequiresParameters
|
||||
attr_reader :name
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :name, :alpha2, :alpha3, :numeric)
|
||||
@name = options.delete(:name)
|
||||
@codes = options.collect{|k,v| CountryCode.new(v)}
|
||||
end
|
||||
|
||||
def code(format)
|
||||
@codes.detect{|c| c.format == format}
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
(@name == other.name)
|
||||
end
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
@name.hash
|
||||
end
|
||||
|
||||
def to_s
|
||||
@name
|
||||
end
|
||||
|
||||
COUNTRIES = [
|
||||
{ :alpha2 => 'AF', :name => 'Afghanistan', :alpha3 => 'AFG', :numeric => '004' },
|
||||
{ :alpha2 => 'AL', :name => 'Albania', :alpha3 => 'ALB', :numeric => '008' },
|
||||
{ :alpha2 => 'DZ', :name => 'Algeria', :alpha3 => 'DZA', :numeric => '012' },
|
||||
{ :alpha2 => 'AS', :name => 'American Samoa', :alpha3 => 'ASM', :numeric => '016' },
|
||||
{ :alpha2 => 'AD', :name => 'Andorra', :alpha3 => 'AND', :numeric => '020' },
|
||||
{ :alpha2 => 'AO', :name => 'Angola', :alpha3 => 'AGO', :numeric => '024' },
|
||||
{ :alpha2 => 'AI', :name => 'Anguilla', :alpha3 => 'AIA', :numeric => '660' },
|
||||
{ :alpha2 => 'AG', :name => 'Antigua and Barbuda', :alpha3 => 'ATG', :numeric => '028' },
|
||||
{ :alpha2 => 'AR', :name => 'Argentina', :alpha3 => 'ARG', :numeric => '032' },
|
||||
{ :alpha2 => 'AM', :name => 'Armenia', :alpha3 => 'ARM', :numeric => '051' },
|
||||
{ :alpha2 => 'AW', :name => 'Aruba', :alpha3 => 'ABW', :numeric => '533' },
|
||||
{ :alpha2 => 'AU', :name => 'Australia', :alpha3 => 'AUS', :numeric => '036' },
|
||||
{ :alpha2 => 'AT', :name => 'Austria', :alpha3 => 'AUT', :numeric => '040' },
|
||||
{ :alpha2 => 'AZ', :name => 'Azerbaijan', :alpha3 => 'AZE', :numeric => '031' },
|
||||
{ :alpha2 => 'BS', :name => 'Bahamas', :alpha3 => 'BHS', :numeric => '044' },
|
||||
{ :alpha2 => 'BH', :name => 'Bahrain', :alpha3 => 'BHR', :numeric => '048' },
|
||||
{ :alpha2 => 'BD', :name => 'Bangladesh', :alpha3 => 'BGD', :numeric => '050' },
|
||||
{ :alpha2 => 'BB', :name => 'Barbados', :alpha3 => 'BRB', :numeric => '052' },
|
||||
{ :alpha2 => 'BY', :name => 'Belarus', :alpha3 => 'BLR', :numeric => '112' },
|
||||
{ :alpha2 => 'BE', :name => 'Belgium', :alpha3 => 'BEL', :numeric => '056' },
|
||||
{ :alpha2 => 'BZ', :name => 'Belize', :alpha3 => 'BLZ', :numeric => '084' },
|
||||
{ :alpha2 => 'BJ', :name => 'Benin', :alpha3 => 'BEN', :numeric => '204' },
|
||||
{ :alpha2 => 'BM', :name => 'Bermuda', :alpha3 => 'BMU', :numeric => '060' },
|
||||
{ :alpha2 => 'BT', :name => 'Bhutan', :alpha3 => 'BTN', :numeric => '064' },
|
||||
{ :alpha2 => 'BO', :name => 'Bolivia', :alpha3 => 'BOL', :numeric => '068' },
|
||||
{ :alpha2 => 'BA', :name => 'Bosnia and Herzegovina', :alpha3 => 'BIH', :numeric => '070' },
|
||||
{ :alpha2 => 'BW', :name => 'Botswana', :alpha3 => 'BWA', :numeric => '072' },
|
||||
{ :alpha2 => 'BV', :name => 'Bouvet Island', :alpha3 => 'BVD', :numeric => '074' },
|
||||
{ :alpha2 => 'BR', :name => 'Brazil', :alpha3 => 'BRA', :numeric => '076' },
|
||||
{ :alpha2 => 'IO', :name => 'British Indian Ocean Territory', :alpha3 => 'IOT', :numeric => '086' },
|
||||
{ :alpha2 => 'BN', :name => 'Brunei Darussalam', :alpha3 => 'BRN', :numeric => '096' },
|
||||
{ :alpha2 => 'BG', :name => 'Bulgaria', :alpha3 => 'BGR', :numeric => '100' },
|
||||
{ :alpha2 => 'BF', :name => 'Burkina Faso', :alpha3 => 'BFA', :numeric => '854' },
|
||||
{ :alpha2 => 'BI', :name => 'Burundi', :alpha3 => 'BDI', :numeric => '108' },
|
||||
{ :alpha2 => 'KH', :name => 'Cambodia', :alpha3 => 'KHM', :numeric => '116' },
|
||||
{ :alpha2 => 'CM', :name => 'Cameroon', :alpha3 => 'CMR', :numeric => '120' },
|
||||
{ :alpha2 => 'CA', :name => 'Canada', :alpha3 => 'CAN', :numeric => '124' },
|
||||
{ :alpha2 => 'CV', :name => 'Cape Verde', :alpha3 => 'CPV', :numeric => '132' },
|
||||
{ :alpha2 => 'KY', :name => 'Cayman Islands', :alpha3 => 'CYM', :numeric => '136' },
|
||||
{ :alpha2 => 'CF', :name => 'Central African Republic', :alpha3 => 'CAF', :numeric => '140' },
|
||||
{ :alpha2 => 'TD', :name => 'Chad', :alpha3 => 'TCD', :numeric => '148' },
|
||||
{ :alpha2 => 'CL', :name => 'Chile', :alpha3 => 'CHL', :numeric => '152' },
|
||||
{ :alpha2 => 'CN', :name => 'China', :alpha3 => 'CHN', :numeric => '156' },
|
||||
{ :alpha2 => 'CX', :name => 'Christmas Island', :alpha3 => 'CXR', :numeric => '162' },
|
||||
{ :alpha2 => 'CC', :name => 'Cocos (Keeling) Islands', :alpha3 => 'CCK', :numeric => '166' },
|
||||
{ :alpha2 => 'CO', :name => 'Colombia', :alpha3 => 'COL', :numeric => '170' },
|
||||
{ :alpha2 => 'KM', :name => 'Comoros', :alpha3 => 'COM', :numeric => '174' },
|
||||
{ :alpha2 => 'CG', :name => 'Congo', :alpha3 => 'COG', :numeric => '178' },
|
||||
{ :alpha2 => 'CD', :name => 'Congo, the Democratic Republic of the', :alpha3 => 'COD', :numeric => '180' },
|
||||
{ :alpha2 => 'CK', :name => 'Cook Islands', :alpha3 => 'COK', :numeric => '184' },
|
||||
{ :alpha2 => 'CR', :name => 'Costa Rica', :alpha3 => 'CRI', :numeric => '188' },
|
||||
{ :alpha2 => 'CI', :name => 'Cote D\'Ivoire', :alpha3 => 'CIV', :numeric => '384' },
|
||||
{ :alpha2 => 'HR', :name => 'Croatia', :alpha3 => 'HRV', :numeric => '191' },
|
||||
{ :alpha2 => 'CU', :name => 'Cuba', :alpha3 => 'CUB', :numeric => '192' },
|
||||
{ :alpha2 => 'CY', :name => 'Cyprus', :alpha3 => 'CYP', :numeric => '196' },
|
||||
{ :alpha2 => 'CZ', :name => 'Czech Republic', :alpha3 => 'CZE', :numeric => '203' },
|
||||
{ :alpha2 => 'DK', :name => 'Denmark', :alpha3 => 'DNK', :numeric => '208' },
|
||||
{ :alpha2 => 'DJ', :name => 'Djibouti', :alpha3 => 'DJI', :numeric => '262' },
|
||||
{ :alpha2 => 'DM', :name => 'Dominica', :alpha3 => 'DMA', :numeric => '212' },
|
||||
{ :alpha2 => 'DO', :name => 'Dominican Republic', :alpha3 => 'DOM', :numeric => '214' },
|
||||
{ :alpha2 => 'EC', :name => 'Ecuador', :alpha3 => 'ECU', :numeric => '218' },
|
||||
{ :alpha2 => 'EG', :name => 'Egypt', :alpha3 => 'EGY', :numeric => '818' },
|
||||
{ :alpha2 => 'SV', :name => 'El Salvador', :alpha3 => 'SLV', :numeric => '222' },
|
||||
{ :alpha2 => 'GQ', :name => 'Equatorial Guinea', :alpha3 => 'GNQ', :numeric => '226' },
|
||||
{ :alpha2 => 'ER', :name => 'Eritrea', :alpha3 => 'ERI', :numeric => '232' },
|
||||
{ :alpha2 => 'EE', :name => 'Estonia', :alpha3 => 'EST', :numeric => '233' },
|
||||
{ :alpha2 => 'ET', :name => 'Ethiopia', :alpha3 => 'ETH', :numeric => '231' },
|
||||
{ :alpha2 => 'FK', :name => 'Falkland Islands (Malvinas)', :alpha3 => 'FLK', :numeric => '238' },
|
||||
{ :alpha2 => 'FO', :name => 'Faroe Islands', :alpha3 => 'FRO', :numeric => '234' },
|
||||
{ :alpha2 => 'FJ', :name => 'Fiji', :alpha3 => 'FJI', :numeric => '242' },
|
||||
{ :alpha2 => 'FI', :name => 'Finland', :alpha3 => 'FIN', :numeric => '246' },
|
||||
{ :alpha2 => 'FR', :name => 'France', :alpha3 => 'FRA', :numeric => '250' },
|
||||
{ :alpha2 => 'GF', :name => 'French Guiana', :alpha3 => 'GUF', :numeric => '254' },
|
||||
{ :alpha2 => 'PF', :name => 'French Polynesia', :alpha3 => 'PYF', :numeric => '258' },
|
||||
{ :alpha2 => 'TF', :name => 'French Southern Territories', :alpha3 => 'ATF', :numeric => '260' },
|
||||
{ :alpha2 => 'GA', :name => 'Gabon', :alpha3 => 'GAB', :numeric => '266' },
|
||||
{ :alpha2 => 'GM', :name => 'Gambia', :alpha3 => 'GMB', :numeric => '270' },
|
||||
{ :alpha2 => 'GE', :name => 'Georgia', :alpha3 => 'GEO', :numeric => '268' },
|
||||
{ :alpha2 => 'DE', :name => 'Germany', :alpha3 => 'DEU', :numeric => '276' },
|
||||
{ :alpha2 => 'GH', :name => 'Ghana', :alpha3 => 'GHA', :numeric => '288' },
|
||||
{ :alpha2 => 'GI', :name => 'Gibraltar', :alpha3 => 'GIB', :numeric => '292' },
|
||||
{ :alpha2 => 'GR', :name => 'Greece', :alpha3 => 'GRC', :numeric => '300' },
|
||||
{ :alpha2 => 'GL', :name => 'Greenland', :alpha3 => 'GRL', :numeric => '304' },
|
||||
{ :alpha2 => 'GD', :name => 'Grenada', :alpha3 => 'GRD', :numeric => '308' },
|
||||
{ :alpha2 => 'GP', :name => 'Guadeloupe', :alpha3 => 'GLP', :numeric => '312' },
|
||||
{ :alpha2 => 'GU', :name => 'Guam', :alpha3 => 'GUM', :numeric => '316' },
|
||||
{ :alpha2 => 'GT', :name => 'Guatemala', :alpha3 => 'GTM', :numeric => '320' },
|
||||
{ :alpha2 => 'GG', :name => 'Guernsey', :alpha3 => 'GGY', :numeric => '831' },
|
||||
{ :alpha2 => 'GN', :name => 'Guinea', :alpha3 => 'GIN', :numeric => '324' },
|
||||
{ :alpha2 => 'GW', :name => 'Guinea-Bissau', :alpha3 => 'GNB', :numeric => '624' },
|
||||
{ :alpha2 => 'GY', :name => 'Guyana', :alpha3 => 'GUY', :numeric => '328' },
|
||||
{ :alpha2 => 'HT', :name => 'Haiti', :alpha3 => 'HTI', :numeric => '332' },
|
||||
{ :alpha2 => 'HM', :name => 'Heard Island And Mcdonald Islands', :alpha3 => 'HMD', :numeric => '334' },
|
||||
{ :alpha2 => 'VA', :name => 'Holy See (Vatican City State)', :alpha3 => 'VAT', :numeric => '336' },
|
||||
{ :alpha2 => 'HN', :name => 'Honduras', :alpha3 => 'HND', :numeric => '340' },
|
||||
{ :alpha2 => 'HK', :name => 'Hong Kong', :alpha3 => 'HKG', :numeric => '344' },
|
||||
{ :alpha2 => 'HU', :name => 'Hungary', :alpha3 => 'HUN', :numeric => '348' },
|
||||
{ :alpha2 => 'IS', :name => 'Iceland', :alpha3 => 'ISL', :numeric => '352' },
|
||||
{ :alpha2 => 'IN', :name => 'India', :alpha3 => 'IND', :numeric => '356' },
|
||||
{ :alpha2 => 'ID', :name => 'Indonesia', :alpha3 => 'IDN', :numeric => '360' },
|
||||
{ :alpha2 => 'IR', :name => 'Iran, Islamic Republic of', :alpha3 => 'IRN', :numeric => '364' },
|
||||
{ :alpha2 => 'IQ', :name => 'Iraq', :alpha3 => 'IRQ', :numeric => '368' },
|
||||
{ :alpha2 => 'IE', :name => 'Ireland', :alpha3 => 'IRL', :numeric => '372' },
|
||||
{ :alpha2 => 'IM', :name => 'Isle Of Man', :alpha3 => 'IMN', :numeric => '833' },
|
||||
{ :alpha2 => 'IL', :name => 'Israel', :alpha3 => 'ISR', :numeric => '376' },
|
||||
{ :alpha2 => 'IT', :name => 'Italy', :alpha3 => 'ITA', :numeric => '380' },
|
||||
{ :alpha2 => 'JM', :name => 'Jamaica', :alpha3 => 'JAM', :numeric => '388' },
|
||||
{ :alpha2 => 'JP', :name => 'Japan', :alpha3 => 'JPN', :numeric => '392' },
|
||||
{ :alpha2 => 'JE', :name => 'Jersey', :alpha3 => 'JEY', :numeric => '832' },
|
||||
{ :alpha2 => 'JO', :name => 'Jordan', :alpha3 => 'JOR', :numeric => '400' },
|
||||
{ :alpha2 => 'KZ', :name => 'Kazakhstan', :alpha3 => 'KAZ', :numeric => '398' },
|
||||
{ :alpha2 => 'KE', :name => 'Kenya', :alpha3 => 'KEN', :numeric => '404' },
|
||||
{ :alpha2 => 'KI', :name => 'Kiribati', :alpha3 => 'KIR', :numeric => '296' },
|
||||
{ :alpha2 => 'KP', :name => 'Korea, Democratic People\'s Republic of', :alpha3 => 'PRK', :numeric => '408' },
|
||||
{ :alpha2 => 'KR', :name => 'Korea, Republic of', :alpha3 => 'KOR', :numeric => '410' },
|
||||
{ :alpha2 => 'KW', :name => 'Kuwait', :alpha3 => 'KWT', :numeric => '414' },
|
||||
{ :alpha2 => 'KG', :name => 'Kyrgyzstan', :alpha3 => 'KGZ', :numeric => '417' },
|
||||
{ :alpha2 => 'LA', :name => 'Lao People\'s Democratic Republic', :alpha3 => 'LAO', :numeric => '418' },
|
||||
{ :alpha2 => 'LV', :name => 'Latvia', :alpha3 => 'LVA', :numeric => '428' },
|
||||
{ :alpha2 => 'LB', :name => 'Lebanon', :alpha3 => 'LBN', :numeric => '422' },
|
||||
{ :alpha2 => 'LS', :name => 'Lesotho', :alpha3 => 'LSO', :numeric => '426' },
|
||||
{ :alpha2 => 'LR', :name => 'Liberia', :alpha3 => 'LBR', :numeric => '430' },
|
||||
{ :alpha2 => 'LY', :name => 'Libyan Arab Jamahiriya', :alpha3 => 'LBY', :numeric => '434' },
|
||||
{ :alpha2 => 'LI', :name => 'Liechtenstein', :alpha3 => 'LIE', :numeric => '438' },
|
||||
{ :alpha2 => 'LT', :name => 'Lithuania', :alpha3 => 'LTU', :numeric => '440' },
|
||||
{ :alpha2 => 'LU', :name => 'Luxembourg', :alpha3 => 'LUX', :numeric => '442' },
|
||||
{ :alpha2 => 'MO', :name => 'Macao', :alpha3 => 'MAC', :numeric => '446' },
|
||||
{ :alpha2 => 'MK', :name => 'Macedonia, the Former Yugoslav Republic of', :alpha3 => 'MKD', :numeric => '807' },
|
||||
{ :alpha2 => 'MG', :name => 'Madagascar', :alpha3 => 'MDG', :numeric => '450' },
|
||||
{ :alpha2 => 'MW', :name => 'Malawi', :alpha3 => 'MWI', :numeric => '454' },
|
||||
{ :alpha2 => 'MY', :name => 'Malaysia', :alpha3 => 'MYS', :numeric => '458' },
|
||||
{ :alpha2 => 'MV', :name => 'Maldives', :alpha3 => 'MDV', :numeric => '462' },
|
||||
{ :alpha2 => 'ML', :name => 'Mali', :alpha3 => 'MLI', :numeric => '466' },
|
||||
{ :alpha2 => 'MT', :name => 'Malta', :alpha3 => 'MLT', :numeric => '470' },
|
||||
{ :alpha2 => 'MH', :name => 'Marshall Islands', :alpha3 => 'MHL', :numeric => '584' },
|
||||
{ :alpha2 => 'MQ', :name => 'Martinique', :alpha3 => 'MTQ', :numeric => '474' },
|
||||
{ :alpha2 => 'MR', :name => 'Mauritania', :alpha3 => 'MRT', :numeric => '478' },
|
||||
{ :alpha2 => 'MU', :name => 'Mauritius', :alpha3 => 'MUS', :numeric => '480' },
|
||||
{ :alpha2 => 'YT', :name => 'Mayotte', :alpha3 => 'MYT', :numeric => '175' },
|
||||
{ :alpha2 => 'MX', :name => 'Mexico', :alpha3 => 'MEX', :numeric => '484' },
|
||||
{ :alpha2 => 'FM', :name => 'Micronesia, Federated States of', :alpha3 => 'FSM', :numeric => '583' },
|
||||
{ :alpha2 => 'MD', :name => 'Moldova, Republic of', :alpha3 => 'MDA', :numeric => '498' },
|
||||
{ :alpha2 => 'MC', :name => 'Monaco', :alpha3 => 'MCO', :numeric => '492' },
|
||||
{ :alpha2 => 'MN', :name => 'Mongolia', :alpha3 => 'MNG', :numeric => '496' },
|
||||
{ :alpha2 => 'ME', :name => 'Montenegro', :alpha3 => 'MNE', :numeric => '499' },
|
||||
{ :alpha2 => 'MS', :name => 'Montserrat', :alpha3 => 'MSR', :numeric => '500' },
|
||||
{ :alpha2 => 'MA', :name => 'Morocco', :alpha3 => 'MAR', :numeric => '504' },
|
||||
{ :alpha2 => 'MZ', :name => 'Mozambique', :alpha3 => 'MOZ', :numeric => '508' },
|
||||
{ :alpha2 => 'MM', :name => 'Myanmar', :alpha3 => 'MMR', :numeric => '104' },
|
||||
{ :alpha2 => 'NA', :name => 'Namibia', :alpha3 => 'NAM', :numeric => '516' },
|
||||
{ :alpha2 => 'NR', :name => 'Nauru', :alpha3 => 'NRU', :numeric => '520' },
|
||||
{ :alpha2 => 'NP', :name => 'Nepal', :alpha3 => 'NPL', :numeric => '524' },
|
||||
{ :alpha2 => 'NL', :name => 'Netherlands', :alpha3 => 'NLD', :numeric => '528' },
|
||||
{ :alpha2 => 'AN', :name => 'Netherlands Antilles', :alpha3 => 'ANT', :numeric => '530' },
|
||||
{ :alpha2 => 'NC', :name => 'New Caledonia', :alpha3 => 'NCL', :numeric => '540' },
|
||||
{ :alpha2 => 'NZ', :name => 'New Zealand', :alpha3 => 'NZL', :numeric => '554' },
|
||||
{ :alpha2 => 'NI', :name => 'Nicaragua', :alpha3 => 'NIC', :numeric => '558' },
|
||||
{ :alpha2 => 'NE', :name => 'Niger', :alpha3 => 'NER', :numeric => '562' },
|
||||
{ :alpha2 => 'NG', :name => 'Nigeria', :alpha3 => 'NGA', :numeric => '566' },
|
||||
{ :alpha2 => 'NU', :name => 'Niue', :alpha3 => 'NIU', :numeric => '570' },
|
||||
{ :alpha2 => 'NF', :name => 'Norfolk Island', :alpha3 => 'NFK', :numeric => '574' },
|
||||
{ :alpha2 => 'MP', :name => 'Northern Mariana Islands', :alpha3 => 'MNP', :numeric => '580' },
|
||||
{ :alpha2 => 'NO', :name => 'Norway', :alpha3 => 'NOR', :numeric => '578' },
|
||||
{ :alpha2 => 'OM', :name => 'Oman', :alpha3 => 'OMN', :numeric => '512' },
|
||||
{ :alpha2 => 'PK', :name => 'Pakistan', :alpha3 => 'PAK', :numeric => '586' },
|
||||
{ :alpha2 => 'PW', :name => 'Palau', :alpha3 => 'PLW', :numeric => '585' },
|
||||
{ :alpha2 => 'PS', :name => 'Palestinian Territory, Occupied', :alpha3 => 'PSE', :numeric => '275' },
|
||||
{ :alpha2 => 'PA', :name => 'Panama', :alpha3 => 'PAN', :numeric => '591' },
|
||||
{ :alpha2 => 'PG', :name => 'Papua New Guinea', :alpha3 => 'PNG', :numeric => '598' },
|
||||
{ :alpha2 => 'PY', :name => 'Paraguay', :alpha3 => 'PRY', :numeric => '600' },
|
||||
{ :alpha2 => 'PE', :name => 'Peru', :alpha3 => 'PER', :numeric => '604' },
|
||||
{ :alpha2 => 'PH', :name => 'Philippines', :alpha3 => 'PHL', :numeric => '608' },
|
||||
{ :alpha2 => 'PN', :name => 'Pitcairn', :alpha3 => 'PCN', :numeric => '612' },
|
||||
{ :alpha2 => 'PL', :name => 'Poland', :alpha3 => 'POL', :numeric => '616' },
|
||||
{ :alpha2 => 'PT', :name => 'Portugal', :alpha3 => 'PRT', :numeric => '620' },
|
||||
{ :alpha2 => 'PR', :name => 'Puerto Rico', :alpha3 => 'PRI', :numeric => '630' },
|
||||
{ :alpha2 => 'QA', :name => 'Qatar', :alpha3 => 'QAT', :numeric => '634' },
|
||||
{ :alpha2 => 'RE', :name => 'Reunion', :alpha3 => 'REU', :numeric => '638' },
|
||||
{ :alpha2 => 'RO', :name => 'Romania', :alpha3 => 'ROM', :numeric => '642' },
|
||||
{ :alpha2 => 'RU', :name => 'Russian Federation', :alpha3 => 'RUS', :numeric => '643' },
|
||||
{ :alpha2 => 'RW', :name => 'Rwanda', :alpha3 => 'RWA', :numeric => '646' },
|
||||
{ :alpha2 => 'BL', :name => 'Saint Barthélemy', :alpha3 => 'BLM', :numeric => '652' },
|
||||
{ :alpha2 => 'SH', :name => 'Saint Helena', :alpha3 => 'SHN', :numeric => '654' },
|
||||
{ :alpha2 => 'KN', :name => 'Saint Kitts and Nevis', :alpha3 => 'KNA', :numeric => '659' },
|
||||
{ :alpha2 => 'LC', :name => 'Saint Lucia', :alpha3 => 'LCA', :numeric => '662' },
|
||||
{ :alpha2 => 'MF', :name => 'Saint Martin (French part)', :alpha3 => 'MAF', :numeric => '663' },
|
||||
{ :alpha2 => 'PM', :name => 'Saint Pierre and Miquelon', :alpha3 => 'SPM', :numeric => '666' },
|
||||
{ :alpha2 => 'VC', :name => 'Saint Vincent and the Grenadines', :alpha3 => 'VCT', :numeric => '670' },
|
||||
{ :alpha2 => 'WS', :name => 'Samoa', :alpha3 => 'WSM', :numeric => '882' },
|
||||
{ :alpha2 => 'SM', :name => 'San Marino', :alpha3 => 'SMR', :numeric => '674' },
|
||||
{ :alpha2 => 'ST', :name => 'Sao Tome and Principe', :alpha3 => 'STP', :numeric => '678' },
|
||||
{ :alpha2 => 'SA', :name => 'Saudi Arabia', :alpha3 => 'SAU', :numeric => '682' },
|
||||
{ :alpha2 => 'SN', :name => 'Senegal', :alpha3 => 'SEN', :numeric => '686' },
|
||||
{ :alpha2 => 'RS', :name => 'Serbia', :alpha3 => 'SRB', :numeric => '688' },
|
||||
{ :alpha2 => 'SC', :name => 'Seychelles', :alpha3 => 'SYC', :numeric => '690' },
|
||||
{ :alpha2 => 'SL', :name => 'Sierra Leone', :alpha3 => 'SLE', :numeric => '694' },
|
||||
{ :alpha2 => 'SG', :name => 'Singapore', :alpha3 => 'SGP', :numeric => '702' },
|
||||
{ :alpha2 => 'SK', :name => 'Slovakia', :alpha3 => 'SVK', :numeric => '703' },
|
||||
{ :alpha2 => 'SI', :name => 'Slovenia', :alpha3 => 'SVN', :numeric => '705' },
|
||||
{ :alpha2 => 'SB', :name => 'Solomon Islands', :alpha3 => 'SLB', :numeric => '090' },
|
||||
{ :alpha2 => 'SO', :name => 'Somalia', :alpha3 => 'SOM', :numeric => '706' },
|
||||
{ :alpha2 => 'ZA', :name => 'South Africa', :alpha3 => 'ZAF', :numeric => '710' },
|
||||
{ :alpha2 => 'GS', :name => 'South Georgia and the South Sandwich Islands', :alpha3 => 'SGS', :numeric => '239' },
|
||||
{ :alpha2 => 'ES', :name => 'Spain', :alpha3 => 'ESP', :numeric => '724' },
|
||||
{ :alpha2 => 'LK', :name => 'Sri Lanka', :alpha3 => 'LKA', :numeric => '144' },
|
||||
{ :alpha2 => 'SD', :name => 'Sudan', :alpha3 => 'SDN', :numeric => '736' },
|
||||
{ :alpha2 => 'SR', :name => 'Suriname', :alpha3 => 'SUR', :numeric => '740' },
|
||||
{ :alpha2 => 'SJ', :name => 'Svalbard and Jan Mayen', :alpha3 => 'SJM', :numeric => '744' },
|
||||
{ :alpha2 => 'SZ', :name => 'Swaziland', :alpha3 => 'SWZ', :numeric => '748' },
|
||||
{ :alpha2 => 'SE', :name => 'Sweden', :alpha3 => 'SWE', :numeric => '752' },
|
||||
{ :alpha2 => 'CH', :name => 'Switzerland', :alpha3 => 'CHE', :numeric => '756' },
|
||||
{ :alpha2 => 'SY', :name => 'Syrian Arab Republic', :alpha3 => 'SYR', :numeric => '760' },
|
||||
{ :alpha2 => 'TW', :name => 'Taiwan, Province of China', :alpha3 => 'TWN', :numeric => '158' },
|
||||
{ :alpha2 => 'TJ', :name => 'Tajikistan', :alpha3 => 'TJK', :numeric => '762' },
|
||||
{ :alpha2 => 'TZ', :name => 'Tanzania, United Republic of', :alpha3 => 'TZA', :numeric => '834' },
|
||||
{ :alpha2 => 'TH', :name => 'Thailand', :alpha3 => 'THA', :numeric => '764' },
|
||||
{ :alpha2 => 'TL', :name => 'Timor Leste', :alpha3 => 'TLS', :numeric => '626' },
|
||||
{ :alpha2 => 'TG', :name => 'Togo', :alpha3 => 'TGO', :numeric => '768' },
|
||||
{ :alpha2 => 'TK', :name => 'Tokelau', :alpha3 => 'TKL', :numeric => '772' },
|
||||
{ :alpha2 => 'TO', :name => 'Tonga', :alpha3 => 'TON', :numeric => '776' },
|
||||
{ :alpha2 => 'TT', :name => 'Trinidad and Tobago', :alpha3 => 'TTO', :numeric => '780' },
|
||||
{ :alpha2 => 'TN', :name => 'Tunisia', :alpha3 => 'TUN', :numeric => '788' },
|
||||
{ :alpha2 => 'TR', :name => 'Turkey', :alpha3 => 'TUR', :numeric => '792' },
|
||||
{ :alpha2 => 'TM', :name => 'Turkmenistan', :alpha3 => 'TKM', :numeric => '795' },
|
||||
{ :alpha2 => 'TC', :name => 'Turks and Caicos Islands', :alpha3 => 'TCA', :numeric => '796' },
|
||||
{ :alpha2 => 'TV', :name => 'Tuvalu', :alpha3 => 'TUV', :numeric => '798' },
|
||||
{ :alpha2 => 'UG', :name => 'Uganda', :alpha3 => 'UGA', :numeric => '800' },
|
||||
{ :alpha2 => 'UA', :name => 'Ukraine', :alpha3 => 'UKR', :numeric => '804' },
|
||||
{ :alpha2 => 'AE', :name => 'United Arab Emirates', :alpha3 => 'ARE', :numeric => '784' },
|
||||
{ :alpha2 => 'GB', :name => 'United Kingdom', :alpha3 => 'GBR', :numeric => '826' },
|
||||
{ :alpha2 => 'US', :name => 'United States', :alpha3 => 'USA', :numeric => '840' },
|
||||
{ :alpha2 => 'UM', :name => 'United States Minor Outlying Islands', :alpha3 => 'UMI', :numeric => '581' },
|
||||
{ :alpha2 => 'UY', :name => 'Uruguay', :alpha3 => 'URY', :numeric => '858' },
|
||||
{ :alpha2 => 'UZ', :name => 'Uzbekistan', :alpha3 => 'UZB', :numeric => '860' },
|
||||
{ :alpha2 => 'VU', :name => 'Vanuatu', :alpha3 => 'VUT', :numeric => '548' },
|
||||
{ :alpha2 => 'VE', :name => 'Venezuela', :alpha3 => 'VEN', :numeric => '862' },
|
||||
{ :alpha2 => 'VN', :name => 'Viet Nam', :alpha3 => 'VNM', :numeric => '704' },
|
||||
{ :alpha2 => 'VG', :name => 'Virgin Islands, British', :alpha3 => 'VGB', :numeric => '092' },
|
||||
{ :alpha2 => 'VI', :name => 'Virgin Islands, U.S.', :alpha3 => 'VIR', :numeric => '850' },
|
||||
{ :alpha2 => 'WF', :name => 'Wallis and Futuna', :alpha3 => 'WLF', :numeric => '876' },
|
||||
{ :alpha2 => 'EH', :name => 'Western Sahara', :alpha3 => 'ESH', :numeric => '732' },
|
||||
{ :alpha2 => 'YE', :name => 'Yemen', :alpha3 => 'YEM', :numeric => '887' },
|
||||
{ :alpha2 => 'ZM', :name => 'Zambia', :alpha3 => 'ZMB', :numeric => '894' },
|
||||
{ :alpha2 => 'ZW', :name => 'Zimbabwe', :alpha3 => 'ZWE', :numeric => '716' },
|
||||
{ :alpha2 => 'AX', :name => 'Åland Islands', :alpha3 => 'ALA', :numeric => '248' }
|
||||
]
|
||||
|
||||
def self.find(name)
|
||||
raise InvalidCountryCodeError, "Cannot lookup country for an empty name" if name.blank?
|
||||
|
||||
case name.length
|
||||
when 2, 3
|
||||
upcase_name = name.upcase
|
||||
country_code = CountryCode.new(name)
|
||||
country = COUNTRIES.detect{|c| c[country_code.format] == upcase_name }
|
||||
else
|
||||
country = COUNTRIES.detect{|c| c[:name] == name }
|
||||
end
|
||||
raise InvalidCountryCodeError, "No country could be found for the country #{name}" if country.nil?
|
||||
Country.new(country.dup)
|
||||
end
|
||||
end
|
||||
end
|
||||
26
vendor/gems/active_utils-1.0.5/lib/active_utils/common/error.rb
vendored
Normal file
26
vendor/gems/active_utils-1.0.5/lib/active_utils/common/error.rb
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
class ActiveMerchantError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
class ConnectionError < ActiveMerchantError # :nodoc:
|
||||
end
|
||||
|
||||
class RetriableConnectionError < ConnectionError # :nodoc:
|
||||
end
|
||||
|
||||
class ResponseError < ActiveMerchantError # :nodoc:
|
||||
attr_reader :response
|
||||
|
||||
def initialize(response, message = nil)
|
||||
@response = response
|
||||
@message = message
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
||||
end
|
||||
end
|
||||
|
||||
class ClientCertificateError < ActiveMerchantError # :nodoc
|
||||
end
|
||||
end
|
||||
58
vendor/gems/active_utils-1.0.5/lib/active_utils/common/network_connection_retries.rb
vendored
Normal file
58
vendor/gems/active_utils-1.0.5/lib/active_utils/common/network_connection_retries.rb
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
module ActiveMerchant
|
||||
module NetworkConnectionRetries
|
||||
DEFAULT_RETRIES = 3
|
||||
DEFAULT_CONNECTION_ERRORS = {
|
||||
EOFError => "The remote server dropped the connection",
|
||||
Errno::ECONNRESET => "The remote server reset the connection",
|
||||
Timeout::Error => "The connection to the remote server timed out",
|
||||
Errno::ETIMEDOUT => "The connection to the remote server timed out"
|
||||
}
|
||||
|
||||
def self.included(base)
|
||||
base.send(:attr_accessor, :retry_safe)
|
||||
end
|
||||
|
||||
def retry_exceptions(options={})
|
||||
connection_errors = DEFAULT_CONNECTION_ERRORS.merge(options[:connection_exceptions] || {})
|
||||
|
||||
retry_network_exceptions(options) do
|
||||
begin
|
||||
yield
|
||||
rescue Errno::ECONNREFUSED => e
|
||||
raise ActiveMerchant::RetriableConnectionError, "The remote server refused the connection"
|
||||
rescue OpenSSL::X509::CertificateError => e
|
||||
NetworkConnectionRetries.log(options[:logger], :error, e.message, options[:tag])
|
||||
raise ActiveMerchant::ClientCertificateError, "The remote server did not accept the provided SSL certificate"
|
||||
rescue *connection_errors.keys => e
|
||||
raise ActiveMerchant::ConnectionError, connection_errors[e.class]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def retry_network_exceptions(options = {})
|
||||
retries = options[:max] || DEFAULT_RETRIES
|
||||
|
||||
begin
|
||||
yield
|
||||
rescue ActiveMerchant::RetriableConnectionError => e
|
||||
retries -= 1
|
||||
retry unless retries.zero?
|
||||
NetworkConnectionRetries.log(options[:logger], :error, e.message, options[:tag])
|
||||
raise ActiveMerchant::ConnectionError, e.message
|
||||
rescue ActiveMerchant::ConnectionError => e
|
||||
retries -= 1
|
||||
retry if (options[:retry_safe] || retry_safe) && !retries.zero?
|
||||
NetworkConnectionRetries.log(options[:logger], :error, e.message, options[:tag])
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
def self.log(logger, level, message, tag=nil)
|
||||
tag ||= self.class.to_s
|
||||
message = "[#{tag}] #{message}"
|
||||
logger.send(level, message) if logger
|
||||
end
|
||||
end
|
||||
end
|
||||
24
vendor/gems/active_utils-1.0.5/lib/active_utils/common/post_data.rb
vendored
Normal file
24
vendor/gems/active_utils-1.0.5/lib/active_utils/common/post_data.rb
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
require 'cgi'
|
||||
|
||||
module ActiveMerchant
|
||||
class PostData < Hash
|
||||
class_attribute :required_fields, :instance_writer => false
|
||||
self.required_fields = []
|
||||
|
||||
def []=(key, value)
|
||||
return if value.blank? && !required?(key)
|
||||
super
|
||||
end
|
||||
|
||||
def to_post_data
|
||||
collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
alias_method :to_s, :to_post_data
|
||||
|
||||
private
|
||||
def required?(key)
|
||||
required_fields.include?(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
69
vendor/gems/active_utils-1.0.5/lib/active_utils/common/posts_data.rb
vendored
Normal file
69
vendor/gems/active_utils-1.0.5/lib/active_utils/common/posts_data.rb
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module PostsData #:nodoc:
|
||||
|
||||
def self.included(base)
|
||||
base.superclass_delegating_accessor :ssl_strict
|
||||
base.ssl_strict = true
|
||||
|
||||
base.class_attribute :retry_safe
|
||||
base.retry_safe = false
|
||||
|
||||
base.superclass_delegating_accessor :open_timeout
|
||||
base.open_timeout = 60
|
||||
|
||||
base.superclass_delegating_accessor :read_timeout
|
||||
base.read_timeout = 60
|
||||
|
||||
base.superclass_delegating_accessor :logger
|
||||
base.superclass_delegating_accessor :wiredump_device
|
||||
end
|
||||
|
||||
def ssl_get(endpoint, headers={})
|
||||
ssl_request(:get, endpoint, nil, headers)
|
||||
end
|
||||
|
||||
def ssl_post(endpoint, data, headers = {})
|
||||
ssl_request(:post, endpoint, data, headers)
|
||||
end
|
||||
|
||||
def ssl_request(method, endpoint, data, headers)
|
||||
handle_response(raw_ssl_request(method, endpoint, data, headers))
|
||||
end
|
||||
|
||||
def raw_ssl_request(method, endpoint, data, headers = {})
|
||||
logger.warn "#{self.class} using ssl_strict=false, which is insecure" if logger unless ssl_strict
|
||||
|
||||
connection = new_connection(endpoint)
|
||||
connection.open_timeout = open_timeout
|
||||
connection.read_timeout = read_timeout
|
||||
connection.retry_safe = retry_safe
|
||||
connection.verify_peer = ssl_strict
|
||||
connection.logger = logger
|
||||
connection.tag = self.class.name
|
||||
connection.wiredump_device = wiredump_device
|
||||
|
||||
connection.pem = @options[:pem] if @options
|
||||
connection.pem_password = @options[:pem_password] if @options
|
||||
|
||||
connection.ignore_http_status = @options[:ignore_http_status] if @options
|
||||
|
||||
connection.request(method, data, headers)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def new_connection(endpoint)
|
||||
Connection.new(endpoint)
|
||||
end
|
||||
|
||||
def handle_response(response)
|
||||
case response.code.to_i
|
||||
when 200...300
|
||||
response.body
|
||||
else
|
||||
raise ResponseError.new(response)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
16
vendor/gems/active_utils-1.0.5/lib/active_utils/common/requires_parameters.rb
vendored
Normal file
16
vendor/gems/active_utils-1.0.5/lib/active_utils/common/requires_parameters.rb
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module RequiresParameters #:nodoc:
|
||||
def requires!(hash, *params)
|
||||
params.each do |param|
|
||||
if param.is_a?(Array)
|
||||
raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
|
||||
|
||||
valid_options = param[1..-1]
|
||||
raise ArgumentError.new("Parameter: #{param.first} must be one of #{valid_options.to_sentence(:words_connector => 'or')}") unless valid_options.include?(hash[param.first])
|
||||
else
|
||||
raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
20
vendor/gems/active_utils-1.0.5/lib/active_utils/common/utils.rb
vendored
Normal file
20
vendor/gems/active_utils-1.0.5/lib/active_utils/common/utils.rb
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
require 'securerandom'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Utils #:nodoc:
|
||||
def generate_unique_id
|
||||
SecureRandom.hex(16)
|
||||
end
|
||||
|
||||
module_function :generate_unique_id
|
||||
|
||||
def deprecated(message)
|
||||
warning = Kernel.caller[1] + message
|
||||
if respond_to?(:logger) && logger.present?
|
||||
logger.warn(warning)
|
||||
else
|
||||
warn(warning)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
81
vendor/gems/active_utils-1.0.5/lib/active_utils/common/validateable.rb
vendored
Normal file
81
vendor/gems/active_utils-1.0.5/lib/active_utils/common/validateable.rb
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Validateable #:nodoc:
|
||||
def valid?
|
||||
errors.clear
|
||||
|
||||
before_validate if respond_to?(:before_validate, true)
|
||||
validate if respond_to?(:validate, true)
|
||||
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
def initialize(attributes = {})
|
||||
self.attributes = attributes
|
||||
end
|
||||
|
||||
def errors
|
||||
@errors ||= Errors.new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def attributes=(attributes)
|
||||
unless attributes.nil?
|
||||
for key, value in attributes
|
||||
send("#{key}=", value )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This hash keeps the errors of the object
|
||||
class Errors < HashWithIndifferentAccess
|
||||
|
||||
def initialize(base)
|
||||
super() { |h, k| h[k] = [] ; h[k] }
|
||||
@base = base
|
||||
end
|
||||
|
||||
def count
|
||||
size
|
||||
end
|
||||
|
||||
def empty?
|
||||
all? { |k, v| v && v.empty? }
|
||||
end
|
||||
|
||||
# returns a specific fields error message.
|
||||
# if more than one error is available we will only return the first. If no error is available
|
||||
# we return an empty string
|
||||
def on(field)
|
||||
self[field].to_a.first
|
||||
end
|
||||
|
||||
def add(field, error)
|
||||
self[field] << error
|
||||
end
|
||||
|
||||
def add_to_base(error)
|
||||
add(:base, error)
|
||||
end
|
||||
|
||||
def each_full
|
||||
full_messages.each { |msg| yield msg }
|
||||
end
|
||||
|
||||
def full_messages
|
||||
result = []
|
||||
|
||||
self.each do |key, messages|
|
||||
next if messages.blank?
|
||||
if key == 'base'
|
||||
result << "#{messages.first}"
|
||||
else
|
||||
result << "#{key.to_s.humanize} #{messages.first}"
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
3
vendor/gems/active_utils-1.0.5/lib/active_utils/version.rb
vendored
Normal file
3
vendor/gems/active_utils-1.0.5/lib/active_utils/version.rb
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module ActiveUtils
|
||||
VERSION = "1.0.5"
|
||||
end
|
||||
7815
vendor/gems/active_utils-1.0.5/lib/certs/cacert.pem
vendored
Normal file
7815
vendor/gems/active_utils-1.0.5/lib/certs/cacert.pem
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
vendor/gems/active_utils-1.0.5/test/test_helper.rb
vendored
Normal file
12
vendor/gems/active_utils-1.0.5/test/test_helper.rb
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env ruby
|
||||
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler'
|
||||
Bundler.setup
|
||||
|
||||
require 'test/unit'
|
||||
require 'active_utils'
|
||||
require 'mocha'
|
||||
|
||||
include ActiveMerchant
|
||||
149
vendor/gems/active_utils-1.0.5/test/unit/connection_test.rb
vendored
Normal file
149
vendor/gems/active_utils-1.0.5/test/unit/connection_test.rb
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
require 'test_helper'
|
||||
|
||||
class ConnectionTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@ok = stub(:code => 200, :message => 'OK', :body => 'success')
|
||||
|
||||
@endpoint = 'https://example.com/tx.php'
|
||||
@connection = ActiveMerchant::Connection.new(@endpoint)
|
||||
@connection.logger = stub(:info => nil, :debug => nil, :error => nil)
|
||||
end
|
||||
|
||||
def test_connection_endpoint_parses_string_to_uri
|
||||
assert_equal URI.parse(@endpoint), @connection.endpoint
|
||||
end
|
||||
|
||||
def test_connection_endpoint_accepts_uri
|
||||
endpoint = URI.parse(@endpoint)
|
||||
connection = ActiveMerchant::Connection.new(endpoint)
|
||||
assert_equal endpoint, connection.endpoint
|
||||
end
|
||||
|
||||
def test_connection_endpoint_raises_uri_error
|
||||
assert_raises URI::InvalidURIError do
|
||||
ActiveMerchant::Connection.new("not a URI")
|
||||
end
|
||||
end
|
||||
|
||||
def test_successful_get_request
|
||||
@connection.logger.expects(:info).twice
|
||||
Net::HTTP.any_instance.expects(:get).with('/tx.php', {}).returns(@ok)
|
||||
response = @connection.request(:get, nil, {})
|
||||
assert_equal 'success', response.body
|
||||
end
|
||||
|
||||
def test_successful_post_request
|
||||
Net::HTTP.any_instance.expects(:post).with('/tx.php', 'data', ActiveMerchant::Connection::RUBY_184_POST_HEADERS).returns(@ok)
|
||||
response = @connection.request(:post, 'data', {})
|
||||
assert_equal 'success', response.body
|
||||
end
|
||||
|
||||
def test_successful_put_request
|
||||
Net::HTTP.any_instance.expects(:put).with('/tx.php', 'data', {}).returns(@ok)
|
||||
response = @connection.request(:put, 'data', {})
|
||||
assert_equal 'success', response.body
|
||||
end
|
||||
|
||||
def test_successful_delete_request
|
||||
Net::HTTP.any_instance.expects(:delete).with('/tx.php', {}).returns(@ok)
|
||||
response = @connection.request(:delete, nil, {})
|
||||
assert_equal 'success', response.body
|
||||
end
|
||||
|
||||
def test_get_raises_argument_error_if_passed_data
|
||||
assert_raise(ArgumentError) do
|
||||
@connection.request(:get, 'data', {})
|
||||
end
|
||||
end
|
||||
|
||||
def test_request_raises_when_request_method_not_supported
|
||||
assert_raise(ArgumentError) do
|
||||
@connection.request(:head, nil, {})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def test_default_read_timeout
|
||||
assert_equal ActiveMerchant::Connection::READ_TIMEOUT, @connection.read_timeout
|
||||
end
|
||||
|
||||
def test_override_read_timeout
|
||||
@connection.read_timeout = 20
|
||||
assert_equal 20, @connection.read_timeout
|
||||
end
|
||||
|
||||
def test_default_open_timeout
|
||||
@connection.open_timeout = 20
|
||||
assert_equal 20, @connection.open_timeout
|
||||
end
|
||||
|
||||
def test_default_verify_peer
|
||||
assert_equal ActiveMerchant::Connection::VERIFY_PEER, @connection.verify_peer
|
||||
end
|
||||
|
||||
def test_override_verify_peer
|
||||
@connection.verify_peer = false
|
||||
assert_equal false, @connection.verify_peer
|
||||
end
|
||||
|
||||
def test_unrecoverable_exception
|
||||
@connection.logger.expects(:error).once
|
||||
Net::HTTP.any_instance.expects(:post).raises(EOFError)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_then_success_with_recoverable_exception
|
||||
@connection.logger.expects(:error).never
|
||||
Net::HTTP.any_instance.expects(:post).times(2).raises(Errno::ECONNREFUSED).then.returns(@ok)
|
||||
|
||||
assert_nothing_raised do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_limit_reached
|
||||
@connection.logger.expects(:error).once
|
||||
Net::HTTP.any_instance.expects(:post).times(ActiveMerchant::Connection::MAX_RETRIES).raises(Errno::ECONNREFUSED)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_then_success_with_retry_safe_enabled
|
||||
Net::HTTP.any_instance.expects(:post).times(2).raises(EOFError).then.returns(@ok)
|
||||
|
||||
@connection.retry_safe = true
|
||||
|
||||
assert_nothing_raised do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_mixture_of_failures_with_retry_safe_enabled
|
||||
Net::HTTP.any_instance.expects(:post).times(3).raises(Errno::ECONNRESET).
|
||||
raises(Errno::ECONNREFUSED).
|
||||
raises(EOFError)
|
||||
|
||||
@connection.retry_safe = true
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_with_ssl_certificate
|
||||
@connection.logger.expects(:error).once
|
||||
Net::HTTP.any_instance.expects(:post).raises(OpenSSL::X509::CertificateError)
|
||||
|
||||
assert_raises(ActiveMerchant::ClientCertificateError) do
|
||||
@connection.request(:post, '')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
31
vendor/gems/active_utils-1.0.5/test/unit/country_code_test.rb
vendored
Normal file
31
vendor/gems/active_utils-1.0.5/test/unit/country_code_test.rb
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
require 'test_helper'
|
||||
|
||||
class CountryCodeTest < Test::Unit::TestCase
|
||||
def test_alpha2_country_code
|
||||
code = CountryCode.new('CA')
|
||||
assert_equal 'CA', code.value
|
||||
assert_equal 'CA', code.to_s
|
||||
assert_equal :alpha2, code.format
|
||||
end
|
||||
|
||||
def test_lower_alpha2_country_code
|
||||
code = CountryCode.new('ca')
|
||||
assert_equal 'CA', code.value
|
||||
assert_equal 'CA', code.to_s
|
||||
assert_equal :alpha2, code.format
|
||||
end
|
||||
|
||||
def test_alpha2_country_code
|
||||
code = CountryCode.new('CAN')
|
||||
assert_equal :alpha3, code.format
|
||||
end
|
||||
|
||||
def test_numeric_code
|
||||
code = CountryCode.new('004')
|
||||
assert_equal :numeric, code.format
|
||||
end
|
||||
|
||||
def test_invalid_code_format
|
||||
assert_raise(CountryCodeFormatError){ CountryCode.new('Canada') }
|
||||
end
|
||||
end
|
||||
68
vendor/gems/active_utils-1.0.5/test/unit/country_test.rb
vendored
Normal file
68
vendor/gems/active_utils-1.0.5/test/unit/country_test.rb
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
require 'test_helper'
|
||||
|
||||
class CountryTest < Test::Unit::TestCase
|
||||
def test_country_from_hash
|
||||
country = Country.new(:name => 'Canada', :alpha2 => 'CA', :alpha3 => 'CAN', :numeric => '124')
|
||||
assert_equal 'CA', country.code(:alpha2).value
|
||||
assert_equal 'CAN', country.code(:alpha3).value
|
||||
assert_equal '124', country.code(:numeric).value
|
||||
assert_equal 'Canada', country.to_s
|
||||
end
|
||||
|
||||
def test_country_for_alpha2_code
|
||||
country = Country.find('CA')
|
||||
assert_equal 'CA', country.code(:alpha2).value
|
||||
assert_equal 'CAN', country.code(:alpha3).value
|
||||
assert_equal '124', country.code(:numeric).value
|
||||
assert_equal 'Canada', country.to_s
|
||||
end
|
||||
|
||||
def test_country_for_alpha3_code
|
||||
country = Country.find('CAN')
|
||||
assert_equal 'Canada', country.to_s
|
||||
end
|
||||
|
||||
def test_country_for_numeric_code
|
||||
country = Country.find('124')
|
||||
assert_equal 'Canada', country.to_s
|
||||
end
|
||||
|
||||
def test_find_country_by_name
|
||||
country = Country.find('Canada')
|
||||
assert_equal 'Canada', country.to_s
|
||||
end
|
||||
|
||||
def test_find_unknown_country_name
|
||||
assert_raise(InvalidCountryCodeError) do
|
||||
Country.find('Asskickistan')
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_australia
|
||||
country = Country.find('AU')
|
||||
assert_equal 'AU', country.code(:alpha2).value
|
||||
|
||||
country = Country.find('Australia')
|
||||
assert_equal 'AU', country.code(:alpha2).value
|
||||
end
|
||||
|
||||
def test_find_united_kingdom
|
||||
country = Country.find('GB')
|
||||
assert_equal 'GB', country.code(:alpha2).value
|
||||
|
||||
country = Country.find('United Kingdom')
|
||||
assert_equal 'GB', country.code(:alpha2).value
|
||||
end
|
||||
|
||||
def test_raise_on_nil_name
|
||||
assert_raise(InvalidCountryCodeError) do
|
||||
Country.find(nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_country_names_are_alphabetized
|
||||
country_names = Country::COUNTRIES.map { | each | each[:name] }
|
||||
assert_equal(country_names.sort, country_names)
|
||||
end
|
||||
|
||||
end
|
||||
127
vendor/gems/active_utils-1.0.5/test/unit/network_connection_retries_test.rb
vendored
Normal file
127
vendor/gems/active_utils-1.0.5/test/unit/network_connection_retries_test.rb
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
require 'test_helper'
|
||||
require 'openssl'
|
||||
require 'net/http'
|
||||
|
||||
class NetworkConnectionRetriesTest < Test::Unit::TestCase
|
||||
class MyNewError < StandardError
|
||||
end
|
||||
|
||||
include NetworkConnectionRetries
|
||||
|
||||
def setup
|
||||
@logger = stubs(:logger)
|
||||
@requester = stubs(:requester)
|
||||
@ok = stub(:code => 200, :message => 'OK', :body => 'success')
|
||||
end
|
||||
|
||||
def test_unrecoverable_exception
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions do
|
||||
raise EOFError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_unrecoverable_exception_logged_if_logger_provided
|
||||
@logger.expects(:error).once
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions :logger => @logger do
|
||||
raise EOFError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_then_success_with_recoverable_exception
|
||||
@requester.expects(:post).times(2).raises(Errno::ECONNREFUSED).then.returns(@ok)
|
||||
|
||||
assert_nothing_raised do
|
||||
retry_exceptions do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_limit_reached
|
||||
@requester.expects(:post).times(ActiveMerchant::NetworkConnectionRetries::DEFAULT_RETRIES).raises(Errno::ECONNREFUSED)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_limit_reached_logs_final_error
|
||||
@logger.expects(:error).once
|
||||
@requester.expects(:post).times(ActiveMerchant::NetworkConnectionRetries::DEFAULT_RETRIES).raises(Errno::ECONNREFUSED)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions(:logger => @logger) do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_then_success_with_retry_safe_enabled
|
||||
@requester.expects(:post).times(2).raises(EOFError).then.returns(@ok)
|
||||
|
||||
assert_nothing_raised do
|
||||
retry_exceptions :retry_safe => true do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_mixture_of_failures_with_retry_safe_enabled
|
||||
@requester.expects(:post).times(3).raises(Errno::ECONNRESET).
|
||||
raises(Errno::ECONNREFUSED).
|
||||
raises(EOFError)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions :retry_safe => true do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_with_ssl_certificate
|
||||
@requester.expects(:post).raises(OpenSSL::X509::CertificateError)
|
||||
|
||||
assert_raises(ActiveMerchant::ClientCertificateError) do
|
||||
retry_exceptions do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_with_ssl_certificate_logs_error_if_logger_specified
|
||||
@logger.expects(:error).once
|
||||
@requester.expects(:post).raises(OpenSSL::X509::CertificateError)
|
||||
|
||||
assert_raises(ActiveMerchant::ClientCertificateError) do
|
||||
retry_exceptions :logger => @logger do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_with_additional_exceptions_specified
|
||||
@requester.expects(:post).raises(MyNewError)
|
||||
|
||||
assert_raises(ActiveMerchant::ConnectionError) do
|
||||
retry_exceptions :connection_exceptions => {MyNewError => "my message"} do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_failure_without_additional_exceptions_specified
|
||||
@requester.expects(:post).raises(MyNewError)
|
||||
|
||||
assert_raises(MyNewError) do
|
||||
retry_exceptions do
|
||||
@requester.post
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
50
vendor/gems/active_utils-1.0.5/test/unit/post_data_test.rb
vendored
Normal file
50
vendor/gems/active_utils-1.0.5/test/unit/post_data_test.rb
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
require 'test_helper'
|
||||
|
||||
class MyPost < ActiveMerchant::PostData
|
||||
self.required_fields = [ :ccnumber, :ccexp, :firstname, :lastname, :username, :password, :order_id, :key, :time ]
|
||||
end
|
||||
|
||||
class PostDataTest < Test::Unit::TestCase
|
||||
def teardown
|
||||
ActiveMerchant::PostData.required_fields = []
|
||||
end
|
||||
|
||||
def test_element_assignment
|
||||
name = 'Cody Fauser'
|
||||
post = ActiveMerchant::PostData.new
|
||||
|
||||
post[:name] = name
|
||||
assert_equal name, post[:name]
|
||||
end
|
||||
|
||||
def test_ignore_blank_fields
|
||||
post = ActiveMerchant::PostData.new
|
||||
assert_equal 0, post.keys.size
|
||||
|
||||
post[:name] = ''
|
||||
assert_equal 0, post.keys.size
|
||||
|
||||
post[:name] = nil
|
||||
assert_equal 0, post.keys.size
|
||||
end
|
||||
|
||||
def test_dont_ignore_required_blank_fields
|
||||
ActiveMerchant::PostData.required_fields = [ :name ]
|
||||
post = ActiveMerchant::PostData.new
|
||||
|
||||
assert_equal 0, post.keys.size
|
||||
|
||||
post[:name] = ''
|
||||
assert_equal 1, post.keys.size
|
||||
assert_equal '', post[:name]
|
||||
|
||||
post[:name] = nil
|
||||
assert_equal 1, post.keys.size
|
||||
assert_nil post[:name]
|
||||
end
|
||||
|
||||
def test_subclass
|
||||
post = MyPost.new
|
||||
assert_equal [ :ccnumber, :ccexp, :firstname, :lastname, :username, :password, :order_id, :key, :time ], post.required_fields
|
||||
end
|
||||
end
|
||||
35
vendor/gems/active_utils-1.0.5/test/unit/posts_data_test.rb
vendored
Normal file
35
vendor/gems/active_utils-1.0.5/test/unit/posts_data_test.rb
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
require 'test_helper'
|
||||
require 'active_support/core_ext/class'
|
||||
|
||||
class PostsDataTest < Test::Unit::TestCase
|
||||
|
||||
class SSLPoster
|
||||
include PostsData
|
||||
|
||||
attr_accessor :logger
|
||||
end
|
||||
|
||||
def setup
|
||||
@poster = SSLPoster.new
|
||||
end
|
||||
|
||||
def test_logger_warns_if_ssl_strict_disabled
|
||||
@poster.logger = stub()
|
||||
@poster.logger.expects(:warn).with("PostsDataTest::SSLPoster using ssl_strict=false, which is insecure")
|
||||
|
||||
Connection.any_instance.stubs(:request)
|
||||
|
||||
SSLPoster.ssl_strict = false
|
||||
@poster.raw_ssl_request(:post, "https://shopify.com", "", {})
|
||||
end
|
||||
|
||||
def test_logger_no_warning_if_ssl_strict_enabled
|
||||
@poster.logger = stub()
|
||||
@poster.logger.stubs(:warn).never
|
||||
Connection.any_instance.stubs(:request)
|
||||
|
||||
SSLPoster.ssl_strict = true
|
||||
@poster.raw_ssl_request(:post, "https://shopify.com", "", {})
|
||||
end
|
||||
|
||||
end
|
||||
7
vendor/gems/active_utils-1.0.5/test/unit/utils_test.rb
vendored
Normal file
7
vendor/gems/active_utils-1.0.5/test/unit/utils_test.rb
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
require 'test_helper'
|
||||
|
||||
class UtilsTest < Test::Unit::TestCase
|
||||
def test_unique_id_should_be_32_chars_and_alphanumeric
|
||||
assert_match /^\w{32}$/, ActiveMerchant::Utils.generate_unique_id
|
||||
end
|
||||
end
|
||||
59
vendor/gems/active_utils-1.0.5/test/unit/validateable_test.rb
vendored
Normal file
59
vendor/gems/active_utils-1.0.5/test/unit/validateable_test.rb
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
require 'test_helper'
|
||||
|
||||
class Dood
|
||||
include ActiveMerchant::Validateable
|
||||
|
||||
attr_accessor :name, :email, :country
|
||||
|
||||
def validate
|
||||
errors.add "name", "cannot be empty" if name.blank?
|
||||
errors.add "email", "cannot be empty" if email.blank?
|
||||
errors.add_to_base "The country cannot be blank" if country.blank?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ValidateableTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@dood = Dood.new
|
||||
end
|
||||
|
||||
def test_validation
|
||||
assert ! @dood.valid?
|
||||
assert ! @dood.errors.empty?
|
||||
end
|
||||
|
||||
def test_assigns
|
||||
@dood = Dood.new(:name => "tobi", :email => "tobi@neech.de", :country => 'DE')
|
||||
|
||||
assert_equal "tobi", @dood.name
|
||||
assert_equal "tobi@neech.de", @dood.email
|
||||
assert @dood.valid?
|
||||
end
|
||||
|
||||
def test_multiple_calls
|
||||
@dood.name = "tobi"
|
||||
assert !@dood.valid?
|
||||
|
||||
@dood.email = "tobi@neech.de"
|
||||
assert !@dood.valid?
|
||||
|
||||
@dood.country = 'DE'
|
||||
assert @dood.valid?
|
||||
end
|
||||
|
||||
def test_messages
|
||||
@dood.valid?
|
||||
assert_equal "cannot be empty", @dood.errors.on('name')
|
||||
assert_equal "cannot be empty", @dood.errors.on('email')
|
||||
assert_equal nil, @dood.errors.on('doesnt_exist')
|
||||
|
||||
end
|
||||
|
||||
def test_full_messages
|
||||
@dood.valid?
|
||||
assert_equal ["Email cannot be empty", "Name cannot be empty", "The country cannot be blank"], @dood.errors.full_messages.sort
|
||||
end
|
||||
|
||||
end
|
||||
1143
vendor/gems/activemerchant-1.33.0/CHANGELOG
vendored
Normal file
1143
vendor/gems/activemerchant-1.33.0/CHANGELOG
vendored
Normal file
File diff suppressed because it is too large
Load Diff
402
vendor/gems/activemerchant-1.33.0/CONTRIBUTORS
vendored
Normal file
402
vendor/gems/activemerchant-1.33.0/CONTRIBUTORS
vendored
Normal file
@@ -0,0 +1,402 @@
|
||||
Protx Gateway
|
||||
|
||||
* Contributed by shiftx (Vincent)
|
||||
|
||||
Verifi Gateway
|
||||
|
||||
* Contributed by Paul Hepworth on 2007-05-12.
|
||||
* Portions of Verifi Gateway Copyright (c) 2007 Paul Hepworth
|
||||
|
||||
Plug 'N Pay Gateway
|
||||
|
||||
* Contributed by Ryan Norbauer
|
||||
|
||||
PayJunction Gateway
|
||||
|
||||
* Contributed by Matt Sanders
|
||||
|
||||
E-xact Gateway
|
||||
|
||||
* Contributed by James Edward Gray II
|
||||
|
||||
Linkpoint Gateway
|
||||
|
||||
* Portions of the LinkPoint Gateway by Ryan Heneise
|
||||
|
||||
eWay Gateway
|
||||
|
||||
* Originally contributed by Lucas Carlson (mailto:lucas@rufy.com)
|
||||
* Managed Payments support by Jason Stirk with improvements by Keith Pitt
|
||||
|
||||
CardStream Gateway
|
||||
|
||||
* Portions of the Cardstream gateway by Jonah Fox and Thomas Nichols
|
||||
|
||||
CyberSource Gateway
|
||||
|
||||
* Contributed by Matt Margolis (matt@mattmargolis.net)
|
||||
|
||||
NetRegistry Gateway
|
||||
|
||||
* Originally contributed by George Ogata (mailto: george.ogata@gmail.com)
|
||||
|
||||
DataCash Gateway (March 2, 2007)
|
||||
|
||||
* MoneySpyder, http://moneyspyder.co.uk and E-consultancy, http://www.e-consultancy.com
|
||||
|
||||
PSL Card Gateway (March 27, 2007)
|
||||
|
||||
* MoneySpyder, http://moneyspyder.co.uk
|
||||
|
||||
Viaklix Gateway (Sep 3, 2007)
|
||||
|
||||
* Originally contributed by Sal Scotto
|
||||
|
||||
Braintree Gateway (Sep 4, 2007)
|
||||
|
||||
* Originally contributed by Michael J. Mangino
|
||||
* Portions of the BrainTree gateway by Jeremy Voorhis
|
||||
|
||||
Concord Efsnet Gateway (Sep 7, 2007)
|
||||
|
||||
* Originally contributed by snacktime
|
||||
|
||||
SecurePayTech Gateway (Oct 23, 2007)
|
||||
|
||||
* Originally contributed by Jasper Bryant-Greene
|
||||
|
||||
SkipJack Gateway (Nov 29, 2007)
|
||||
|
||||
* Originally contributed by Bill Bereza - http://atomicobject.com
|
||||
|
||||
HiTRUST Gateway (Dec 10, 2007)
|
||||
|
||||
* Jaded Pixel
|
||||
|
||||
Payflow NV Gateway (Mar 03, 2008)
|
||||
|
||||
* Greg Furmanek
|
||||
|
||||
PaypalNVGateway (Apr 12, 2008)
|
||||
|
||||
* Greg Furmanek
|
||||
|
||||
Beanstream (May 13, 2008)
|
||||
|
||||
* Created by xiaobozz ( xiaobozzz at gmail dot com )
|
||||
* Secure Profiles support by Forrest Zeisler (http://github.com/forrest)
|
||||
|
||||
Sage (June, 2008)
|
||||
|
||||
* Cody
|
||||
|
||||
Modern Payments (June 13, 2008)
|
||||
|
||||
* Initial implementation by Jeremy Nicoll
|
||||
* Additional portions by Cody Fauser
|
||||
|
||||
Wirecard Gateway (June 30, 2008)
|
||||
|
||||
* Initial implementation by Soleone
|
||||
|
||||
Transax Gateway (May 3, 2009)
|
||||
|
||||
* Mike Mangino
|
||||
|
||||
Merchant E-Solutions Gateway (May 3, 2009)
|
||||
|
||||
* Zac Williams, Robby Russell
|
||||
|
||||
Instapay Gateway (May 3, 2009)
|
||||
|
||||
* Thomas Rideout
|
||||
|
||||
Iridium Gateway (June 13, 2009)
|
||||
|
||||
* Phil Smy
|
||||
|
||||
MerchantWARE (July 7, 2009)
|
||||
|
||||
* Cody Fauser
|
||||
|
||||
FirstPay (July 24, 2009)
|
||||
|
||||
* Phil R
|
||||
|
||||
Ogone (July 20, 2009)
|
||||
|
||||
* Nicolas Jacobeus
|
||||
|
||||
Elavon (August 09, 2009)
|
||||
|
||||
* Jesse Storimer
|
||||
|
||||
JetPay (September 29, 2009)
|
||||
|
||||
* Phil Ripperger, Peter Williams
|
||||
|
||||
SallieMae (October 2, 2009)
|
||||
|
||||
* iamjwc
|
||||
|
||||
Netaxept (February 08, 2010)
|
||||
|
||||
* Nathaniel Talbott
|
||||
|
||||
Garanti (May 05, 2010)
|
||||
|
||||
* Selem Delul (moon@mac.home)
|
||||
|
||||
Braintree Blue Gateway (May 19th, 2010)
|
||||
|
||||
* Braintree (code@getbraintree.com)
|
||||
|
||||
Inspire Gateway (September 27, 2010)
|
||||
|
||||
* ryan r. smith
|
||||
|
||||
SecureNet Gateway (September 27, 2010)
|
||||
|
||||
* Kal
|
||||
|
||||
PayboxDirect Gateway (September 27, 2010)
|
||||
|
||||
* Donald Piret <donald@donaldpiret.com>
|
||||
|
||||
SagePay Form Offsite Gateway (October 14, 2010)
|
||||
|
||||
* Adrian Irving-Beer
|
||||
|
||||
DirecPay Gateway (October 14, 2010)
|
||||
|
||||
* Soleone
|
||||
|
||||
ePay Gateway (November 23, 2010)
|
||||
|
||||
* Original code by ePay (epay.dk)
|
||||
* Refactored by Jonathan Rudenberg
|
||||
|
||||
iDEAL/Rabobank Gateway (January 10, 2011)
|
||||
|
||||
* Original code by Soemirno Kartosoewito
|
||||
* Refactored by Cody Fauser
|
||||
* Refactored and updated by Jonathan Rudenberg
|
||||
|
||||
Quantum Gateway
|
||||
|
||||
* Joshua Lippiner
|
||||
* Refactored by Nathaniel Talbott
|
||||
|
||||
BluePay Gateway
|
||||
|
||||
* Mel Sleight
|
||||
* Refactored by Nathaniel Talbott
|
||||
|
||||
Valitor Integration (January 2011)
|
||||
|
||||
* Nathaniel Talbott
|
||||
* Sponsored by Sævar Öfjörð Magnússon
|
||||
|
||||
Barclays ePDQ
|
||||
|
||||
* Original code by Rob W (rfwatson)
|
||||
* Refactored by Nathaniel Talbott
|
||||
|
||||
Federated Canada
|
||||
|
||||
* Bob Larrick (deathbob)
|
||||
|
||||
NMI
|
||||
|
||||
* Nathaniel Talbott (ntalbott)
|
||||
|
||||
QBMS
|
||||
|
||||
* Will Glozer (wg)
|
||||
|
||||
WorldPay Integration (Feb 17, 2011)
|
||||
|
||||
* Original code by Unknown from this patch: https://jadedpixel.lighthouseapp.com/projects/11599/tickets/3-patch-integration-support-for-worldpay-uk
|
||||
* Refactored by Soleone
|
||||
|
||||
WorldPay Gateway
|
||||
|
||||
* Original code by Amit kumar (ask4amit@gmail.com)
|
||||
* Refactored by Nathaniel Talbott (ntalbott)
|
||||
|
||||
Orbital Paymentech Gateway (July, 2009)
|
||||
|
||||
* Sam Vincent - http://ecommerce.versapay.com
|
||||
|
||||
DIRECTebanking - Payment Network AG (May, 2011)
|
||||
|
||||
* Gerwin Brunner (Vilango)
|
||||
|
||||
Stripe
|
||||
|
||||
* Ross Boucher (boucher)
|
||||
|
||||
Paystation (July, 2011)
|
||||
|
||||
* Nik Wakelin (nikz)
|
||||
|
||||
ePaymentPlans offsite gatway (June, 2011)
|
||||
|
||||
* Roberto Miranda (robertomiranda)
|
||||
|
||||
Optimal Payments (August, 2011)
|
||||
|
||||
* Jamie Macey (jamie)
|
||||
|
||||
CardSave (August, 2011)
|
||||
|
||||
* Tom Crinson (MrJaba)
|
||||
|
||||
Dwolla (September, 2011)
|
||||
|
||||
* James Armstead (armsteadj1)
|
||||
|
||||
Samurai (November, 2011)
|
||||
|
||||
* Joshua Krall (jkrall)
|
||||
|
||||
CertoDirect Gateway (February, 2012)
|
||||
|
||||
* Aleksei Gusev (hron)
|
||||
|
||||
Authorize.Net SIM Integration (February, 2012)
|
||||
|
||||
* Roger Pack (rdp)
|
||||
* Nick Rogers (courtland)
|
||||
|
||||
NAB Transact (AU) Gateway (February, 2012)
|
||||
|
||||
* Tom Meier (tommeier)
|
||||
|
||||
iTransact XML Gateway (March, 2012)
|
||||
|
||||
* Kevin Motschiedler (motske)
|
||||
|
||||
Robokassa Integration (March, 2012)
|
||||
|
||||
* Vasiliy Ermolovich (nashby)
|
||||
|
||||
Moneris US Gateway (March, 2012)
|
||||
|
||||
* Michael Wood (eddanger)
|
||||
|
||||
Dotpay Integration (March, 2012)
|
||||
|
||||
* Przemysław Ciąćka (kacperix)
|
||||
|
||||
Vindicia gateway (April 2012)
|
||||
|
||||
* Steven Davidovitz (steved555)
|
||||
|
||||
MiGS gateway (April 2012)
|
||||
|
||||
* Michael Noack (mnoack)
|
||||
* Justin Jones (nagash)
|
||||
|
||||
ePay integration (April 2012)
|
||||
|
||||
* Michael (ePay)
|
||||
|
||||
Litle gateway (May 2012)
|
||||
|
||||
* Gregory Drake (GregDrake)
|
||||
|
||||
Fat Zebra gateway (June 2012)
|
||||
|
||||
* Matthew Savage (amasses)
|
||||
|
||||
Metrics Global gateway (June 2012)
|
||||
|
||||
* Dan Knox (DanKnox)
|
||||
|
||||
EasyPay integration (July 2012)
|
||||
|
||||
* Vasiliy Ermolovich (nashby)
|
||||
|
||||
PayGateXML gateway (July 2012)
|
||||
|
||||
* bryan (rubyisbeautiful)
|
||||
|
||||
PayWay gateway (July 2012)
|
||||
|
||||
* Ben Zhang (BenZhang)
|
||||
|
||||
First Data integration (July 2012)
|
||||
|
||||
* Nick Rogers (courtland)
|
||||
|
||||
WebPay integration (July 2012)
|
||||
|
||||
* Vasiliy Ermolovich (nashby)
|
||||
|
||||
Suomen Maksuturva integration (July 2012)
|
||||
|
||||
* Antti Akonniemi (akonan)
|
||||
|
||||
Paxum integration (July 2012)
|
||||
|
||||
* Stanislav Mekhonoshin (Mehonoshin)
|
||||
|
||||
Balanced gateway (July 2012)
|
||||
|
||||
* Marshall Jones (mjallday)
|
||||
|
||||
PayFast integration (October 2012)
|
||||
|
||||
* Vasiliy Ermolovich (nashby)
|
||||
|
||||
A1Agregator (November 2012)
|
||||
|
||||
* Roman Ivanilov (england)
|
||||
|
||||
Liqpay integration (November 2012)
|
||||
|
||||
* beorc
|
||||
|
||||
eWay Rapid 3.0 gateway (December 2012)
|
||||
|
||||
* Nathaniel Talbott (ntalbott)
|
||||
|
||||
FirstData Global Gateway e4 (December 2012)
|
||||
|
||||
* Chris Sheppard (frobcode)
|
||||
|
||||
Spreedly Core gateway (December 2012)
|
||||
|
||||
* Duff OMelia (duff)
|
||||
|
||||
Pin gateway (February 2013)
|
||||
|
||||
* Myles Eftos (madpilot)
|
||||
|
||||
Merchant Warrior (February 2013)
|
||||
|
||||
* Ben Bruscella (benbruscella)
|
||||
* Дмитрий Василец (pronix)
|
||||
* Kirill Shirinkin (Fodoj)
|
||||
* Nathaniel Talbott (ntalbott)
|
||||
|
||||
Paymill (February 2013)
|
||||
|
||||
* Duff O'Melia (duff)
|
||||
|
||||
EVO Canada (February 2013)
|
||||
|
||||
* Alex Dunae (alexdunae)
|
||||
|
||||
Finansbank WebPOS (March 2013)
|
||||
|
||||
* scamurcuoglu
|
||||
|
||||
Cardstream Modern (March 2013)
|
||||
|
||||
* Vincens (ExxKA)
|
||||
|
||||
Transnational (May 2013)
|
||||
|
||||
* Ben VandenBos (bvandenbos)
|
||||
20
vendor/gems/activemerchant-1.33.0/MIT-LICENSE
vendored
Normal file
20
vendor/gems/activemerchant-1.33.0/MIT-LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2005-2010 Tobias Luetke
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
221
vendor/gems/activemerchant-1.33.0/README.md
vendored
Normal file
221
vendor/gems/activemerchant-1.33.0/README.md
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
# Active Merchant
|
||||
|
||||
Active Merchant is an extraction from the e-commerce system [Shopify](http://www.shopify.com).
|
||||
Shopify's requirements for a simple and unified API to access dozens of different payment
|
||||
gateways with very different internal APIs was the chief principle in designing the library.
|
||||
|
||||
It was developed for usage in Ruby on Rails web applications and integrates seamlessly
|
||||
as a Rails plugin, but it also works excellently as a stand alone Ruby library.
|
||||
|
||||
Active Merchant has been in production use since June 2006 and is now used in most modern
|
||||
Ruby applications which deal with financial transactions. It is maintained by the
|
||||
[Shopify](http://www.shopify.com) and [Spreedly](https://spreedly.com) teams, with much help
|
||||
from an ever-growing set of contributors.
|
||||
|
||||
See [GettingStarted.md](GettingStarted.md) if you want to learn more about using Active Merchant in your
|
||||
applications.
|
||||
|
||||
## Installation
|
||||
|
||||
### From Git
|
||||
|
||||
You can check out the latest source from git:
|
||||
|
||||
git clone git://github.com/Shopify/active_merchant.git
|
||||
|
||||
### From RubyGems
|
||||
|
||||
Installation from RubyGems:
|
||||
|
||||
gem install activemerchant
|
||||
|
||||
Or, if you're using Bundler, just add the following to your Gemfile:
|
||||
|
||||
gem 'activemerchant'
|
||||
|
||||
## Usage
|
||||
|
||||
This simple example demonstrates how a purchase can be made using a person's
|
||||
credit card details.
|
||||
|
||||
require 'rubygems'
|
||||
require 'active_merchant'
|
||||
|
||||
# Use the TrustCommerce test servers
|
||||
ActiveMerchant::Billing::Base.mode = :test
|
||||
|
||||
gateway = ActiveMerchant::Billing::TrustCommerceGateway.new(
|
||||
:login => 'TestMerchant',
|
||||
:password => 'password')
|
||||
|
||||
# ActiveMerchant accepts all amounts as Integer values in cents
|
||||
amount = 1000 # $10.00
|
||||
|
||||
# The card verification value is also known as CVV2, CVC2, or CID
|
||||
credit_card = ActiveMerchant::Billing::CreditCard.new(
|
||||
:first_name => 'Bob',
|
||||
:last_name => 'Bobsen',
|
||||
:number => '4242424242424242',
|
||||
:month => '8',
|
||||
:year => Time.now.year+1,
|
||||
:verification_value => '000')
|
||||
|
||||
# Validating the card automatically detects the card type
|
||||
if credit_card.valid?
|
||||
# Capture $10 from the credit card
|
||||
response = gateway.purchase(amount, credit_card)
|
||||
|
||||
if response.success?
|
||||
puts "Successfully charged $#{sprintf("%.2f", amount / 100)} to the credit card #{credit_card.display_number}"
|
||||
else
|
||||
raise StandardError, response.message
|
||||
end
|
||||
end
|
||||
|
||||
For more in-depth documentation and tutorials, see [GettingStarted.md](GettingStarted.md) and the
|
||||
[API documentation](http://rubydoc.info/github/Shopify/active_merchant/master/file/README.md).
|
||||
|
||||
## Supported Direct Payment Gateways
|
||||
|
||||
The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) contains a [table of features supported by each gateway](http://github.com/Shopify/active_merchant/wikis/gatewayfeaturematrix).
|
||||
|
||||
* [Authorize.Net](http://www.authorize.net/) - US
|
||||
* [Authorize.Net CIM](http://www.authorize.net/) - US
|
||||
* [Balanced](https://www.balancedpayments.com/) - US
|
||||
* [Banwire](https://www.banwire.com/) - MX
|
||||
* [Barclays ePDQ](http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/) - UK
|
||||
* [Beanstream.com](http://www.beanstream.com/) - CA
|
||||
* [BluePay](http://www.bluepay.com/) - US
|
||||
* [Braintree](http://www.braintreepaymentsolutions.com) - US
|
||||
* [CardStream](http://www.cardstream.com/) - UK
|
||||
* [CertoDirect](http://www.certodirect.com/) - BE, BG, CZ, DK, DE, EE, IE, EL, ES, FR, IT, CY, LV, LT, LU, HU, MT, NL, AT, PL, PT, RO, SI, SK, FI, SE, UK
|
||||
* [CyberSource](http://www.cybersource.com) - US
|
||||
* [DataCash](http://www.datacash.com/) - UK
|
||||
* [Efsnet](http://www.concordefsnet.com/) - US
|
||||
* [Elavon MyVirtualMerchant](http://www.elavon.com) - US, CA
|
||||
* [ePay](http://www.epay.dk/) - DK, SE, NO
|
||||
* [EVO Canada](http://www.evocanada.com/) - CA
|
||||
* [eWAY](http://www.eway.com.au/) - AU
|
||||
* [eWay Rapid 3.0](http://www.eway.com.au/) - AU
|
||||
* [E-xact](http://www.e-xact.com) - CA, US
|
||||
* [Fat Zebra](https://www.fatzebra.com.au) - AU
|
||||
* [Federated Canada](http://www.federatedcanada.com/) - CA
|
||||
* [Finansbank WebPOS](https://www.fbwebpos.com/) - US, TR
|
||||
* [FirstData Global Gateway e4](http://www.firstdata.com) - CA, US
|
||||
* [FirstPay](http://www.first-pay.com) - US
|
||||
* [Garanti Sanal POS](https://ccpos.garanti.com.tr/ccRaporlar/garanti/ccReports) - US, TR
|
||||
* [HDFC](http://www.hdfcbank.com/sme/sme-details/merchant-services/guzh6m0i) - IN
|
||||
* [Inspire](http://www.inspiregateway.com) - US
|
||||
* [InstaPay](http://www.instapayllc.com) - US
|
||||
* [Iridium](http://www.iridiumcorp.co.uk/) - UK, ES
|
||||
* [iTransact](http://www.itransact.com/) - US
|
||||
* [JetPay](http://www.jetpay.com) - US
|
||||
* [LinkPoint](http://www.linkpoint.com/) - US
|
||||
* [Litle](http://www.litle.com/) - US
|
||||
* [Merchant e-Solutions](http://merchante-solutions.com/) - US
|
||||
* [MerchantWare](http://merchantwarehouse.com/merchantware) - US
|
||||
* [Merchant Warrior] (http://merchantwarrior.com) - AU
|
||||
* [Mercury](http://www.mercurypay.com) - US
|
||||
* [MasterCard Internet Gateway Service (MiGS)](http://mastercard.com/mastercardsps) - AU, AE, BD, BN, EG, HK, ID, IN, JO, KW, LB, LK, MU, MV, MY, NZ, OM, PH, QA, SA, SG, TT, VN
|
||||
* [Modern Payments](http://www.modpay.com) - US
|
||||
* [Moneris](http://www.moneris.com/) - CA
|
||||
* [Moneris US](http://www.monerisusa.com/) - US
|
||||
* [NABTransact](http://www.nab.com.au/nabtransact/) - AU
|
||||
* [NELiX TransaX Gateway](http://www.nelixtransax.com) - US
|
||||
* [Netaxept](http://www.betalingsterminal.no/Netthandel-forside) - NO, DK, SE, FI
|
||||
* [NETbilling](http://www.netbilling.com) - US
|
||||
* [NetPay](http://www.netpay.com.mx) - MX
|
||||
* [NetRegistry](http://www.netregistry.com.au) - AU
|
||||
* [NMI](http://nmi.com/) - US
|
||||
* [Ogone DirectLink](http://www.ogone.com) - BE, DE, FR, NL, AT, CH
|
||||
* [Optimal Payments](http://www.optimalpayments.com/) - CA, US, UK
|
||||
* [Orbital Paymentech](http://chasepaymentech.com/) - CA, US, UK, GB
|
||||
* [PayBox Direct](http://www.paybox.com) - FR
|
||||
* [PayFast](https://www.payfast.co.za/) - ZA
|
||||
* [PayGate PayXML](http://paygate.co.za/) - US, ZA
|
||||
* [PayJunction](http://www.payjunction.com/) - US
|
||||
* [PaymentExpress](http://www.paymentexpress.com/) - AU, MY, NZ, SG, ZA, UK, US
|
||||
* [PAYMILL](https://www.paymill.com) - AD, AT, BE, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, SE, SI, SK, TR, VA
|
||||
* [PayPal Express Checkout](https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside) - US, CA, SG, AU
|
||||
* [PayPal Payflow Pro](https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside) - US, CA, SG, AU
|
||||
* [PayPal Website Payments Pro (UK)](https://www.paypal.com/uk/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - UK
|
||||
* [PayPal Website Payments Pro (CA)](https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - CA
|
||||
* [PayPal Express Checkout](https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside) - US
|
||||
* [PayPal Website Payments Pro (US)](https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - US
|
||||
* [PaySecure](http://www.commsecure.com.au/paysecure.shtml) - AU
|
||||
* [PayWay](https://www.payway.com.au) - AU
|
||||
* [Pin](http://www.pin.net.au/) - AU
|
||||
* [Plug'n Pay](http://www.plugnpay.com/) - US
|
||||
* [Psigate](http://www.psigate.com/) - CA
|
||||
* [PSL Payment Solutions](http://www.paymentsolutionsltd.com/) - UK
|
||||
* [Quantum](http://www.quantumgateway.com) - US
|
||||
* [QuickBooks Merchant Services](http://payments.intuit.com/) - US
|
||||
* [Quickpay](http://quickpay.dk/) - DK, SE
|
||||
* [Rabobank Nederland](http://www.rabobank.nl/) - NL
|
||||
* [Realex](http://www.realexpayments.com/) - IE, UK
|
||||
* [Redsys](http://www.redsys.es) - ES
|
||||
* [SagePay](http://www.sagepay.com) - UK
|
||||
* [Sage Payment Solutions](http://www.sagepayments.com) - US, CA
|
||||
* [Sallie Mae](http://www.salliemae.com) - US
|
||||
* [SecureNet](http://www.securenet.com) - US
|
||||
* [SecurePay](http://securepay.com.au) - AU
|
||||
* [SecurePay](http://www.securepay.com/) - US
|
||||
* [SecurePayTech](http://www.securepaytech.com/) - NZ
|
||||
* [SkipJack](http://www.skipjack.com/) - US, CA
|
||||
* [Spreedly Core](https://spreedlycore.com/) - AD, AE, AT, AU, BD, BE, BG, BN, CA, CH, CY, CZ, DE, DK, EE, EG, ES, FI, FR, GB, GI, GR, HK, HU, ID, IE, IL, IM, IN, IS, IT, JO, KW, LB, LI, LK, LT, LU, LV, MC, MT, MU, MV, MX, MY, NL, NO, NZ, OM, PH, PL, PT, QA, RO, SA, SE, SG, SI, SK, SM, TR, TT, UM, US, VA, VN, ZA
|
||||
* [Stripe](https://stripe.com/) - US
|
||||
* [TransFirst](http://www.transfirst.com/) - US
|
||||
* [Transnational](http://www.tnbci.com/) - US
|
||||
* [TrustCommerce](http://www.trustcommerce.com/) - US
|
||||
* [USA ePay](http://www.usaepay.com/) - US
|
||||
* [Verifi](http://www.verifi.com/) - US
|
||||
* [ViaKLIX](http://viaklix.com) - US
|
||||
* [Vindica](http://www.vindicia.com/) - US, CA, UK, AU, MX, BR, DE, KR, CN, HK
|
||||
* [WebPay](https://webpay.jp/) - JP
|
||||
* [Wirecard](http://www.wirecard.com) - DE
|
||||
* [WorldPay](http://www.worldpay.com) - AU, HK, UK, US
|
||||
|
||||
## Supported Offsite Payment Gateways
|
||||
|
||||
* [2 Checkout](http://www.2checkout.com)
|
||||
* [A1Agregator](http://a1agregator.ru/) - RU
|
||||
* [Authorize.Net SIM](http://developer.authorize.net/api/sim/) - US
|
||||
* [Banca Sella GestPay](https://www.sella.it/banca/ecommerce/gestpay/gestpay.jsp)
|
||||
* [Chronopay](http://www.chronopay.com)
|
||||
* [DirecPay](http://www.timesofmoney.com/direcpay/jsp/home.jsp)
|
||||
* [Direct-eBanking / sofortueberweisung.de by Payment-Networks AG](https://www.payment-network.com/deb_com_en/merchantarea/home) - DE, AT, CH, BE, UK, NL
|
||||
* [Dotpay](http://dotpay.pl)
|
||||
* [Dwolla](https://www.dwolla.com/default.aspx)
|
||||
* [ePay](http://www.epay.dk/epay-payment-solutions/)
|
||||
* [First Data](https://firstdata.zendesk.com/entries/407522-first-data-global-gateway-e4sm-payment-pages-integration-manual)
|
||||
* [HiTRUST](http://www.hitrust.com.hk/)
|
||||
* [Moneybookers](http://www.moneybookers.com)
|
||||
* [Nochex](http://www.nochex.com)
|
||||
* [Paxum](https://www.paxum.com/)
|
||||
* [PayPal Website Payments Standard](https://www.paypal.com/cgi-bin/webscr?cmd#_wp-standard-overview-outside)
|
||||
* [Paysbuy](https://www.paysbuy.com/) - TH
|
||||
* [RBK Money](https://rbkmoney.ru/) - RU
|
||||
* [Robokassa](http://robokassa.ru/) - RU
|
||||
* [SagePay Form](http://www.sagepay.com/products_services/sage_pay_go/integration/form)
|
||||
* [Suomen Maksuturva](https://www.maksuturva.fi/services/vendor_services/integration_guidelines.html)
|
||||
* [Valitor](http://www.valitor.is/) - IS
|
||||
* [Verkkomaksut](http://www.verkkomaksut.fi) - FI
|
||||
* [WebMoney](http://www.webmoney.ru) - RU
|
||||
* [WebPay](http://webpay.by/)
|
||||
* [WorldPay](http://www.worldpay.com)
|
||||
|
||||
## Contributing
|
||||
|
||||
The source code is hosted at [GitHub](http://github.com/Shopify/active_merchant), and can be fetched using:
|
||||
|
||||
git clone git://github.com/Shopify/active_merchant.git
|
||||
|
||||
Please see the [ActiveMerchant Guide to Contributing](http://github.com/Shopify/active_merchant/wikis/contributing) for
|
||||
information on adding a new gateway to ActiveMerchant.
|
||||
|
||||
Please don't touch the CHANGELOG in your pull requests, we'll add the appropriate CHANGELOG entries
|
||||
at release time.
|
||||
|
||||
[](http://travis-ci.org/Shopify/active_merchant)
|
||||
|
||||
[](https://codeclimate.com/github/Shopify/active_merchant)
|
||||
20
vendor/gems/activemerchant-1.33.0/gem-public_cert.pem
vendored
Normal file
20
vendor/gems/activemerchant-1.33.0/gem-public_cert.pem
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDApjb2R5
|
||||
ZmF1c2VyMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
|
||||
b20wHhcNMDcwMjIyMTcyMTI3WhcNMDgwMjIyMTcyMTI3WjBBMRMwEQYDVQQDDApj
|
||||
b2R5ZmF1c2VyMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
|
||||
FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6T4Iqt5iWvAlU
|
||||
iXI6L8UO0URQhIC65X/gJ9hL/x4lwSl/ckVm/R/bPrJGmifT+YooFv824N3y/TIX
|
||||
25o/lZtRj1TUZJK4OCb0aVzosQVxBHSe6rLmxO8cItNTMOM9wn3thaITFrTa1DOQ
|
||||
O3wqEjvW2L6VMozVfK1MfjL9IGgy0rCnl+2g4Gh4jDDpkLfnMG5CWI6cTCf3C1ye
|
||||
ytOpWgi0XpOEy8nQWcFmt/KCQ/kFfzBo4QxqJi54b80842EyvzWT9OB7Oew/CXZG
|
||||
F2yIHtiYxonz6N09vvSzq4CvEuisoUFLKZnktndxMEBKwJU3XeSHAbuS7ix40OKO
|
||||
WKuI54fHAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
||||
BBR9QQpefI3oDCAxiqJW/3Gg6jI6qjANBgkqhkiG9w0BAQUFAAOCAQEAs0lX26O+
|
||||
HpyMp7WL+SgZuM8k76AjfOHuKajl2GEn3S8pWYGpsa0xu07HtehJhKLiavrfUYeE
|
||||
qlFtyYMUyOh6/1S2vfkH6VqjX7mWjoi7XKHW/99fkMS40B5SbN+ypAUst+6c5R84
|
||||
w390mjtLHpdDE6WQYhS6bFvBN53vK6jG3DLyCJc0K9uMQ7gdHWoxq7RnG92ncQpT
|
||||
ThpRA+fky5Xt2Q63YJDnJpkYAz79QIama1enSnd4jslKzSl89JS2luq/zioPe/Us
|
||||
hbyalWR1+HrhgPoSPq7nk+s2FQUBJ9UZFK1lgMzho/4fZgzJwbu+cO8SNuaLS/bj
|
||||
hPaSTyVU0yCSnw==
|
||||
-----END CERTIFICATE-----
|
||||
63
vendor/gems/activemerchant-1.33.0/lib/active_merchant.rb
vendored
Normal file
63
vendor/gems/activemerchant-1.33.0/lib/active_merchant.rb
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#--
|
||||
# Copyright (c) 2005-2010 Tobias Luetke
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#++
|
||||
|
||||
require 'active_support'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/hash/indifferent_access'
|
||||
require 'active_support/core_ext/hash/conversions'
|
||||
require 'active_support/core_ext/object/conversions'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/core_ext/class/delegating_attributes'
|
||||
require 'active_support/core_ext/module/attribute_accessors'
|
||||
|
||||
begin
|
||||
require 'active_support/base64'
|
||||
|
||||
unless defined?(Base64)
|
||||
Base64 = ActiveSupport::Base64
|
||||
end
|
||||
|
||||
unless Base64.respond_to?(:strict_encode64)
|
||||
def Base64.strict_encode64(v)
|
||||
ActiveSupport::Base64.encode64s(v)
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
require 'base64'
|
||||
end
|
||||
|
||||
require 'securerandom'
|
||||
require 'builder'
|
||||
require 'cgi'
|
||||
require 'rexml/document'
|
||||
|
||||
require 'active_utils'
|
||||
require 'active_merchant/billing'
|
||||
require 'active_merchant/version'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
autoload :Integrations, 'active_merchant/billing/integrations'
|
||||
end
|
||||
end
|
||||
9
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing.rb
vendored
Normal file
9
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
require 'active_merchant/billing/avs_result'
|
||||
require 'active_merchant/billing/cvv_result'
|
||||
require 'active_merchant/billing/credit_card_methods'
|
||||
require 'active_merchant/billing/credit_card_formatting'
|
||||
require 'active_merchant/billing/credit_card'
|
||||
require 'active_merchant/billing/base'
|
||||
require 'active_merchant/billing/check'
|
||||
require 'active_merchant/billing/response'
|
||||
require 'active_merchant/billing/gateways'
|
||||
98
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/avs_result.rb
vendored
Normal file
98
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/avs_result.rb
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
#!ruby19
|
||||
# encoding: utf-8
|
||||
|
||||
module ActiveMerchant
|
||||
module Billing
|
||||
# Implements the Address Verification System
|
||||
# https://www.wellsfargo.com/downloads/pdf/biz/merchant/visa_avs.pdf
|
||||
# http://en.wikipedia.org/wiki/Address_Verification_System
|
||||
# http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_IG/html/app_avs_cvn_codes.htm#app_AVS_CVN_codes_7891_48375
|
||||
# http://imgserver.skipjack.com/imgServer/5293710/AVS%20and%20CVV2.pdf
|
||||
# http://www.emsecommerce.net/avs_cvv2_response_codes.htm
|
||||
class AVSResult
|
||||
MESSAGES = {
|
||||
'A' => 'Street address matches, but 5-digit and 9-digit postal code do not match.',
|
||||
'B' => 'Street address matches, but postal code not verified.',
|
||||
'C' => 'Street address and postal code do not match.',
|
||||
'D' => 'Street address and postal code match.',
|
||||
'E' => 'AVS data is invalid or AVS is not allowed for this card type.',
|
||||
'F' => 'Card member\'s name does not match, but billing postal code matches.',
|
||||
'G' => 'Non-U.S. issuing bank does not support AVS.',
|
||||
'H' => 'Card member\'s name does not match. Street address and postal code match.',
|
||||
'I' => 'Address not verified.',
|
||||
'J' => 'Card member\'s name, billing address, and postal code match. Shipping information verified and chargeback protection guaranteed through the Fraud Protection Program.',
|
||||
'K' => 'Card member\'s name matches but billing address and billing postal code do not match.',
|
||||
'L' => 'Card member\'s name and billing postal code match, but billing address does not match.',
|
||||
'M' => 'Street address and postal code match.',
|
||||
'N' => 'Street address and postal code do not match.',
|
||||
'O' => 'Card member\'s name and billing address match, but billing postal code does not match.',
|
||||
'P' => 'Postal code matches, but street address not verified.',
|
||||
'Q' => 'Card member\'s name, billing address, and postal code match. Shipping information verified but chargeback protection not guaranteed.',
|
||||
'R' => 'System unavailable.',
|
||||
'S' => 'U.S.-issuing bank does not support AVS.',
|
||||
'T' => 'Card member\'s name does not match, but street address matches.',
|
||||
'U' => 'Address information unavailable.',
|
||||
'V' => 'Card member\'s name, billing address, and billing postal code match.',
|
||||
'W' => 'Street address does not match, but 9-digit postal code matches.',
|
||||
'X' => 'Street address and 9-digit postal code match.',
|
||||
'Y' => 'Street address and 5-digit postal code match.',
|
||||
'Z' => 'Street address does not match, but 5-digit postal code matches.'
|
||||
}
|
||||
|
||||
# Map vendor's AVS result code to a postal match code
|
||||
POSTAL_MATCH_CODE = {
|
||||
'Y' => %w( D H F H J L M P Q V W X Y Z ),
|
||||
'N' => %w( A C K N O ),
|
||||
'X' => %w( G S ),
|
||||
nil => %w( B E I R T U )
|
||||
}.inject({}) do |map, (type, codes)|
|
||||
codes.each { |code| map[code] = type }
|
||||
map
|
||||
end
|
||||
|
||||
# Map vendor's AVS result code to a street match code
|
||||
STREET_MATCH_CODE = {
|
||||
'Y' => %w( A B D H J M O Q T V X Y ),
|
||||
'N' => %w( C K L N W Z ),
|
||||
'X' => %w( G S ),
|
||||
nil => %w( E F I P R U )
|
||||
}.inject({}) do |map, (type, codes)|
|
||||
codes.each { |code| map[code] = type }
|
||||
map
|
||||
end
|
||||
|
||||
attr_reader :code, :message, :street_match, :postal_match
|
||||
|
||||
def self.messages
|
||||
MESSAGES
|
||||
end
|
||||
|
||||
def initialize(attrs)
|
||||
attrs ||= {}
|
||||
|
||||
@code = attrs[:code].upcase unless attrs[:code].blank?
|
||||
@message = self.class.messages[code]
|
||||
|
||||
if attrs[:street_match].blank?
|
||||
@street_match = STREET_MATCH_CODE[code]
|
||||
else
|
||||
@street_match = attrs[:street_match].upcase
|
||||
end
|
||||
|
||||
if attrs[:postal_match].blank?
|
||||
@postal_match = POSTAL_MATCH_CODE[code]
|
||||
else
|
||||
@postal_match = attrs[:postal_match].upcase
|
||||
end
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{ 'code' => code,
|
||||
'message' => message,
|
||||
'street_match' => street_match,
|
||||
'postal_match' => postal_match
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
56
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/base.rb
vendored
Normal file
56
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/base.rb
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
module Base
|
||||
# Set ActiveMerchant gateways in test mode.
|
||||
#
|
||||
# ActiveMerchant::Billing::Base.gateway_mode = :test
|
||||
mattr_accessor :gateway_mode
|
||||
|
||||
# Set ActiveMerchant integrations in test mode.
|
||||
#
|
||||
# ActiveMerchant::Billing::Base.integration_mode = :test
|
||||
mattr_accessor :integration_mode
|
||||
|
||||
# Set both the mode of both the gateways and integrations
|
||||
# at once
|
||||
mattr_reader :mode
|
||||
|
||||
def self.mode=(mode)
|
||||
@@mode = mode
|
||||
self.gateway_mode = mode
|
||||
self.integration_mode = mode
|
||||
end
|
||||
|
||||
self.mode = :production
|
||||
|
||||
# Return the matching gateway for the provider
|
||||
# * <tt>bogus</tt>: BogusGateway - Does nothing (for testing)
|
||||
# * <tt>moneris</tt>: MonerisGateway
|
||||
# * <tt>authorize_net</tt>: AuthorizeNetGateway
|
||||
# * <tt>trust_commerce</tt>: TrustCommerceGateway
|
||||
#
|
||||
# ActiveMerchant::Billing::Base.gateway('moneris').new
|
||||
def self.gateway(name)
|
||||
Billing.const_get("#{name.to_s.downcase}_gateway".camelize)
|
||||
end
|
||||
|
||||
# Return the matching integration module
|
||||
# You can then get the notification from the module
|
||||
# * <tt>bogus</tt>: Bogus - Does nothing (for testing)
|
||||
# * <tt>chronopay</tt>: Chronopay
|
||||
# * <tt>paypal</tt>: Paypal
|
||||
#
|
||||
# chronopay = ActiveMerchant::Billing::Base.integration('chronopay')
|
||||
# notification = chronopay.notification(raw_post)
|
||||
#
|
||||
def self.integration(name)
|
||||
Billing::Integrations.const_get("#{name.to_s.downcase}".camelize)
|
||||
end
|
||||
|
||||
# A check to see if we're in test mode
|
||||
def self.test?
|
||||
self.gateway_mode == :test
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
69
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/check.rb
vendored
Normal file
69
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/check.rb
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# The Check object is a plain old Ruby object, similar to CreditCard. It supports validation
|
||||
# of necessary attributes such as checkholder's name, routing and account numbers, but it is
|
||||
# not backed by any database.
|
||||
#
|
||||
# You may use Check in place of CreditCard with any gateway that supports it.
|
||||
class Check
|
||||
include Validateable
|
||||
|
||||
attr_accessor :first_name, :last_name,
|
||||
:bank_name, :routing_number, :account_number,
|
||||
:account_holder_type, :account_type, :number
|
||||
|
||||
# Used for Canadian bank accounts
|
||||
attr_accessor :institution_number, :transit_number
|
||||
|
||||
def name
|
||||
@name ||= "#{@first_name} #{@last_name}".strip
|
||||
end
|
||||
|
||||
def name=(value)
|
||||
return if value.blank?
|
||||
|
||||
@name = value
|
||||
segments = value.split(' ')
|
||||
@last_name = segments.pop
|
||||
@first_name = segments.join(' ')
|
||||
end
|
||||
|
||||
def validate
|
||||
[:name, :routing_number, :account_number].each do |attr|
|
||||
errors.add(attr, "cannot be empty") if self.send(attr).blank?
|
||||
end
|
||||
|
||||
errors.add(:routing_number, "is invalid") unless valid_routing_number?
|
||||
|
||||
errors.add(:account_holder_type, "must be personal or business") if
|
||||
!account_holder_type.blank? && !%w[business personal].include?(account_holder_type.to_s)
|
||||
|
||||
errors.add(:account_type, "must be checking or savings") if
|
||||
!account_type.blank? && !%w[checking savings].include?(account_type.to_s)
|
||||
end
|
||||
|
||||
def type
|
||||
'check'
|
||||
end
|
||||
|
||||
# Routing numbers may be validated by calculating a checksum and dividing it by 10. The
|
||||
# formula is:
|
||||
# (3(d1 + d4 + d7) + 7(d2 + d5 + d8) + 1(d3 + d6 + d9))mod 10 = 0
|
||||
# See http://en.wikipedia.org/wiki/Routing_transit_number#Internal_checksums
|
||||
def valid_routing_number?
|
||||
d = routing_number.to_s.split('').map(&:to_i).select { |d| (0..9).include?(d) }
|
||||
case d.size
|
||||
when 9 then
|
||||
checksum = ((3 * (d[0] + d[3] + d[6])) +
|
||||
(7 * (d[1] + d[4] + d[7])) +
|
||||
(d[2] + d[5] + d[8])) % 10
|
||||
case checksum
|
||||
when 0 then true
|
||||
else false
|
||||
end
|
||||
else false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
278
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card.rb
vendored
Normal file
278
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card.rb
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
require 'time'
|
||||
require 'date'
|
||||
require 'active_merchant/billing/expiry_date'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# A +CreditCard+ object represents a physical credit card, and is capable of validating the various
|
||||
# data associated with these.
|
||||
#
|
||||
# At the moment, the following credit card types are supported:
|
||||
#
|
||||
# * Visa
|
||||
# * MasterCard
|
||||
# * Discover
|
||||
# * American Express
|
||||
# * Diner's Club
|
||||
# * JCB
|
||||
# * Switch
|
||||
# * Solo
|
||||
# * Dankort
|
||||
# * Maestro
|
||||
# * Forbrugsforeningen
|
||||
# * Laser
|
||||
#
|
||||
# For testing purposes, use the 'bogus' credit card brand. This skips the vast majority of
|
||||
# validations, allowing you to focus on your core concerns until you're ready to be more concerned
|
||||
# with the details of particular credit cards or your gateway.
|
||||
#
|
||||
# == Testing With CreditCard
|
||||
# Often when testing we don't care about the particulars of a given card brand. When using the 'test'
|
||||
# mode in your {Gateway}, there are six different valid card numbers: 1, 2, 3, 'success', 'fail',
|
||||
# and 'error'.
|
||||
#
|
||||
# For details, see {CreditCardMethods::ClassMethods#valid_number?}
|
||||
#
|
||||
# == Example Usage
|
||||
# cc = CreditCard.new(
|
||||
# :first_name => 'Steve',
|
||||
# :last_name => 'Smith',
|
||||
# :month => '9',
|
||||
# :year => '2010',
|
||||
# :brand => 'visa',
|
||||
# :number => '4242424242424242'
|
||||
# )
|
||||
#
|
||||
# cc.valid? # => true
|
||||
# cc.display_number # => XXXX-XXXX-XXXX-4242
|
||||
#
|
||||
class CreditCard
|
||||
include CreditCardMethods
|
||||
include Validateable
|
||||
|
||||
cattr_accessor :require_verification_value
|
||||
self.require_verification_value = true
|
||||
|
||||
# Returns or sets the credit card number.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :number
|
||||
|
||||
# Returns or sets the expiry month for the card.
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_accessor :month
|
||||
|
||||
# Returns or sets the expiry year for the card.
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_accessor :year
|
||||
|
||||
# Returns or sets the credit card brand.
|
||||
#
|
||||
# Valid card types are
|
||||
#
|
||||
# * +'visa'+
|
||||
# * +'master'+
|
||||
# * +'discover'+
|
||||
# * +'american_express'+
|
||||
# * +'diners_club'+
|
||||
# * +'jcb'+
|
||||
# * +'switch'+
|
||||
# * +'solo'+
|
||||
# * +'dankort'+
|
||||
# * +'maestro'+
|
||||
# * +'forbrugsforeningen'+
|
||||
# * +'laser'+
|
||||
#
|
||||
# Or, if you wish to test your implementation, +'bogus'+.
|
||||
#
|
||||
# @return (String) the credit card brand
|
||||
attr_accessor :brand
|
||||
|
||||
# Returns or sets the first name of the card holder.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :first_name
|
||||
|
||||
# Returns or sets the last name of the card holder.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :last_name
|
||||
|
||||
# Required for Switch / Solo cards
|
||||
attr_accessor :start_month, :start_year, :issue_number
|
||||
|
||||
# Returns or sets the card verification value.
|
||||
#
|
||||
# This attribute is optional but recommended. The verification value is
|
||||
# a {card security code}[http://en.wikipedia.org/wiki/Card_security_code]. If provided,
|
||||
# the gateway will attempt to validate the value.
|
||||
#
|
||||
# @return [String] the verification value
|
||||
attr_accessor :verification_value
|
||||
|
||||
def type
|
||||
self.class.deprecated "CreditCard#type is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand instead."
|
||||
brand
|
||||
end
|
||||
|
||||
def type=(value)
|
||||
self.class.deprecated "CreditCard#type is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand instead."
|
||||
self.brand = value
|
||||
end
|
||||
|
||||
# Provides proxy access to an expiry date object
|
||||
#
|
||||
# @return [ExpiryDate]
|
||||
def expiry_date
|
||||
ExpiryDate.new(@month, @year)
|
||||
end
|
||||
|
||||
# Returns whether the credit card has expired.
|
||||
#
|
||||
# @return +true+ if the card has expired, +false+ otherwise
|
||||
def expired?
|
||||
expiry_date.expired?
|
||||
end
|
||||
|
||||
# Returns whether either the +first_name+ or the +last_name+ attributes has been set.
|
||||
def name?
|
||||
first_name? || last_name?
|
||||
end
|
||||
|
||||
# Returns whether the +first_name+ attribute has been set.
|
||||
def first_name?
|
||||
@first_name.present?
|
||||
end
|
||||
|
||||
# Returns whether the +last_name+ attribute has been set.
|
||||
def last_name?
|
||||
@last_name.present?
|
||||
end
|
||||
|
||||
# Returns the full name of the card holder.
|
||||
#
|
||||
# @return [String] the full name of the card holder
|
||||
def name
|
||||
[@first_name, @last_name].compact.join(' ')
|
||||
end
|
||||
|
||||
def name=(full_name)
|
||||
names = full_name.split
|
||||
self.last_name = names.pop
|
||||
self.first_name = names.join(" ")
|
||||
end
|
||||
|
||||
def verification_value?
|
||||
!@verification_value.blank?
|
||||
end
|
||||
|
||||
# Returns a display-friendly version of the card number.
|
||||
#
|
||||
# All but the last 4 numbers are replaced with an "X", and hyphens are
|
||||
# inserted in order to improve legibility.
|
||||
#
|
||||
# @example
|
||||
# credit_card = CreditCard.new(:number => "2132542376824338")
|
||||
# credit_card.display_number # "XXXX-XXXX-XXXX-4338"
|
||||
#
|
||||
# @return [String] a display-friendly version of the card number
|
||||
def display_number
|
||||
self.class.mask(number)
|
||||
end
|
||||
|
||||
def first_digits
|
||||
self.class.first_digits(number)
|
||||
end
|
||||
|
||||
def last_digits
|
||||
self.class.last_digits(number)
|
||||
end
|
||||
|
||||
# Validates the credit card details.
|
||||
#
|
||||
# Any validation errors are added to the {#errors} attribute.
|
||||
def validate
|
||||
validate_essential_attributes
|
||||
|
||||
# Bogus card is pretty much for testing purposes. Lets just skip these extra tests if its used
|
||||
return if brand == 'bogus'
|
||||
|
||||
validate_card_brand
|
||||
validate_card_number
|
||||
validate_verification_value
|
||||
validate_switch_or_solo_attributes
|
||||
end
|
||||
|
||||
def self.requires_verification_value?
|
||||
require_verification_value
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def before_validate #:nodoc:
|
||||
self.month = month.to_i
|
||||
self.year = year.to_i
|
||||
self.start_month = start_month.to_i unless start_month.nil?
|
||||
self.start_year = start_year.to_i unless start_year.nil?
|
||||
self.number = number.to_s.gsub(/[^\d]/, "")
|
||||
self.brand.downcase! if brand.respond_to?(:downcase)
|
||||
self.brand = self.class.brand?(number) if brand.blank?
|
||||
end
|
||||
|
||||
def validate_card_number #:nodoc:
|
||||
if number.blank?
|
||||
errors.add :number, "is required"
|
||||
elsif !CreditCard.valid_number?(number)
|
||||
errors.add :number, "is not a valid credit card number"
|
||||
end
|
||||
|
||||
unless errors.on(:number) || errors.on(:brand)
|
||||
errors.add :brand, "does not match the card number" unless CreditCard.matching_brand?(number, brand)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_card_brand #:nodoc:
|
||||
errors.add :brand, "is required" if brand.blank? && number.present?
|
||||
errors.add :brand, "is invalid" unless brand.blank? || CreditCard.card_companies.keys.include?(brand)
|
||||
end
|
||||
|
||||
alias_method :validate_card_type, :validate_card_brand
|
||||
|
||||
def validate_essential_attributes #:nodoc:
|
||||
errors.add :first_name, "cannot be empty" if @first_name.blank?
|
||||
errors.add :last_name, "cannot be empty" if @last_name.blank?
|
||||
|
||||
if @month.to_i.zero? || @year.to_i.zero?
|
||||
errors.add :month, "is required" if @month.to_i.zero?
|
||||
errors.add :year, "is required" if @year.to_i.zero?
|
||||
else
|
||||
errors.add :month, "is not a valid month" unless valid_month?(@month)
|
||||
errors.add :year, "expired" if expired?
|
||||
errors.add :year, "is not a valid year" unless expired? || valid_expiry_year?(@year)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_switch_or_solo_attributes #:nodoc:
|
||||
if %w[switch solo].include?(brand)
|
||||
unless valid_month?(@start_month) && valid_start_year?(@start_year) || valid_issue_number?(@issue_number)
|
||||
if @issue_number.blank?
|
||||
errors.add :start_month, "is invalid" unless valid_month?(@start_month)
|
||||
errors.add :start_year, "is invalid" unless valid_start_year?(@start_year)
|
||||
errors.add :issue_number, "cannot be empty"
|
||||
else
|
||||
errors.add :issue_number, "is invalid" unless valid_issue_number?(@issue_number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_verification_value #:nodoc:
|
||||
if CreditCard.requires_verification_value?
|
||||
errors.add :verification_value, "is required" unless verification_value?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card_formatting.rb
vendored
Normal file
21
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card_formatting.rb
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
module CreditCardFormatting
|
||||
|
||||
# This method is used to format numerical information pertaining to credit cards.
|
||||
#
|
||||
# format(2005, :two_digits) # => "05"
|
||||
# format(05, :four_digits) # => "0005"
|
||||
def format(number, option)
|
||||
return '' if number.blank?
|
||||
|
||||
case option
|
||||
when :two_digits ; sprintf("%.2i", number.to_i)[-2..-1]
|
||||
when :four_digits ; sprintf("%.4i", number.to_i)[-4..-1]
|
||||
else number
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
143
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card_methods.rb
vendored
Normal file
143
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/credit_card_methods.rb
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# Convenience methods that can be included into a custom Credit Card object, such as an ActiveRecord based Credit Card object.
|
||||
module CreditCardMethods
|
||||
CARD_COMPANIES = {
|
||||
'visa' => /^4\d{12}(\d{3})?$/,
|
||||
'master' => /^(5[1-5]\d{4}|677189)\d{10}$/,
|
||||
'discover' => /^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/,
|
||||
'american_express' => /^3[47]\d{13}$/,
|
||||
'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
|
||||
'jcb' => /^35(28|29|[3-8]\d)\d{12}$/,
|
||||
'switch' => /^6759\d{12}(\d{2,3})?$/,
|
||||
'solo' => /^6767\d{12}(\d{2,3})?$/,
|
||||
'dankort' => /^5019\d{12}$/,
|
||||
'maestro' => /^(5[06-8]|6\d)\d{10,17}$/,
|
||||
'forbrugsforeningen' => /^600722\d{10}$/,
|
||||
'laser' => /^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/
|
||||
}
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def valid_month?(month)
|
||||
(1..12).include?(month.to_i)
|
||||
end
|
||||
|
||||
def valid_expiry_year?(year)
|
||||
(Time.now.year..Time.now.year + 20).include?(year.to_i)
|
||||
end
|
||||
|
||||
def valid_start_year?(year)
|
||||
year.to_s =~ /^\d{4}$/ && year.to_i > 1987
|
||||
end
|
||||
|
||||
def valid_issue_number?(number)
|
||||
number.to_s =~ /^\d{1,2}$/
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Returns true if it validates. Optionally, you can pass a card brand as an argument and
|
||||
# make sure it is of the correct brand.
|
||||
#
|
||||
# References:
|
||||
# - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
|
||||
# - http://www.beachnet.com/~hstiles/cardtype.html
|
||||
def valid_number?(number)
|
||||
valid_test_mode_card_number?(number) ||
|
||||
valid_card_number_length?(number) &&
|
||||
valid_checksum?(number)
|
||||
end
|
||||
|
||||
# Regular expressions for the known card companies.
|
||||
#
|
||||
# References:
|
||||
# - http://en.wikipedia.org/wiki/Credit_card_number
|
||||
# - http://www.barclaycardbusiness.co.uk/information_zone/processing/bin_rules.html
|
||||
def card_companies
|
||||
CARD_COMPANIES
|
||||
end
|
||||
|
||||
# Returns a string containing the brand of card from the list of known information below.
|
||||
# Need to check the cards in a particular order, as there is some overlap of the allowable ranges
|
||||
#--
|
||||
# TODO Refactor this method. We basically need to tighten up the Maestro Regexp.
|
||||
#
|
||||
# Right now the Maestro regexp overlaps with the MasterCard regexp (IIRC). If we can tighten
|
||||
# things up, we can boil this whole thing down to something like...
|
||||
#
|
||||
# def brand?(number)
|
||||
# return 'visa' if valid_test_mode_card_number?(number)
|
||||
# card_companies.find([nil]) { |brand, regexp| number =~ regexp }.first.dup
|
||||
# end
|
||||
#
|
||||
def brand?(number)
|
||||
return 'bogus' if valid_test_mode_card_number?(number)
|
||||
|
||||
card_companies.reject { |c,p| c == 'maestro' }.each do |company, pattern|
|
||||
return company.dup if number =~ pattern
|
||||
end
|
||||
|
||||
return 'maestro' if number =~ card_companies['maestro']
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def type?(number)
|
||||
deprecated "CreditCard#type? is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand? instead."
|
||||
brand?(number)
|
||||
end
|
||||
|
||||
def first_digits(number)
|
||||
number.to_s.slice(0,6)
|
||||
end
|
||||
|
||||
def last_digits(number)
|
||||
number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
|
||||
end
|
||||
|
||||
def mask(number)
|
||||
"XXXX-XXXX-XXXX-#{last_digits(number)}"
|
||||
end
|
||||
|
||||
# Checks to see if the calculated brand matches the specified brand
|
||||
def matching_brand?(number, brand)
|
||||
brand?(number) == brand
|
||||
end
|
||||
|
||||
def matching_type?(number, brand)
|
||||
deprecated "CreditCard#matching_type? is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#matching_brand? instead."
|
||||
matching_brand?(number, brand)
|
||||
end
|
||||
|
||||
def deprecated(message)
|
||||
warn(Kernel.caller[1] + message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_card_number_length?(number) #:nodoc:
|
||||
number.to_s.length >= 12
|
||||
end
|
||||
|
||||
def valid_test_mode_card_number?(number) #:nodoc:
|
||||
ActiveMerchant::Billing::Base.test? &&
|
||||
%w[1 2 3 success failure error].include?(number.to_s)
|
||||
end
|
||||
|
||||
# Checks the validity of a card number by use of the the Luhn Algorithm.
|
||||
# Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
|
||||
def valid_checksum?(number) #:nodoc:
|
||||
sum = 0
|
||||
for i in 0..number.length
|
||||
weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
|
||||
sum += (weight < 10) ? weight : weight - 9
|
||||
end
|
||||
|
||||
(number[-1,1].to_i == (10 - sum % 10) % 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
38
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/cvv_result.rb
vendored
Normal file
38
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/cvv_result.rb
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
module ActiveMerchant
|
||||
module Billing
|
||||
# Result of the Card Verification Value check
|
||||
# http://www.bbbonline.org/eExport/doc/MerchantGuide_cvv2.pdf
|
||||
# Check additional codes from cybersource website
|
||||
class CVVResult
|
||||
|
||||
MESSAGES = {
|
||||
'D' => 'Suspicious transaction',
|
||||
'I' => 'Failed data validation check',
|
||||
'M' => 'Match',
|
||||
'N' => 'No Match',
|
||||
'P' => 'Not Processed',
|
||||
'S' => 'Should have been present',
|
||||
'U' => 'Issuer unable to process request',
|
||||
'X' => 'Card does not support verification'
|
||||
}
|
||||
|
||||
def self.messages
|
||||
MESSAGES
|
||||
end
|
||||
|
||||
attr_reader :code, :message
|
||||
|
||||
def initialize(code)
|
||||
@code = code.upcase unless code.blank?
|
||||
@message = MESSAGES[@code]
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
'code' => code,
|
||||
'message' => message
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/expiry_date.rb
vendored
Normal file
34
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/expiry_date.rb
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
require 'date'
|
||||
|
||||
module ActiveMerchant
|
||||
module Billing
|
||||
class CreditCard
|
||||
class ExpiryDate #:nodoc:
|
||||
attr_reader :month, :year
|
||||
def initialize(month, year)
|
||||
@month = month.to_i
|
||||
@year = year.to_i
|
||||
end
|
||||
|
||||
def expired? #:nodoc:
|
||||
Time.now.utc > expiration
|
||||
end
|
||||
|
||||
def expiration #:nodoc:
|
||||
begin
|
||||
Time.utc(year, month, month_days, 23, 59, 59)
|
||||
rescue ArgumentError
|
||||
Time.at(0).utc
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def month_days
|
||||
mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
|
||||
mdays[2] = 29 if Date.leap?(year)
|
||||
mdays[month]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
177
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateway.rb
vendored
Normal file
177
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateway.rb
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
require 'net/http'
|
||||
require 'net/https'
|
||||
require 'active_merchant/billing/response'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
#
|
||||
# == Description
|
||||
# The Gateway class is the base class for all ActiveMerchant gateway implementations.
|
||||
#
|
||||
# The standard list of gateway functions that most concrete gateway subclasses implement is:
|
||||
#
|
||||
# * <tt>purchase(money, creditcard, options = {})</tt>
|
||||
# * <tt>authorize(money, creditcard, options = {})</tt>
|
||||
# * <tt>capture(money, authorization, options = {})</tt>
|
||||
# * <tt>void(identification, options = {})</tt>
|
||||
# * <tt>credit(money, identification, options = {})</tt>
|
||||
#
|
||||
# Some gateways include features for recurring billing
|
||||
#
|
||||
# * <tt>recurring(money, creditcard, options = {})</tt>
|
||||
#
|
||||
# Some gateways also support features for storing credit cards:
|
||||
#
|
||||
# * <tt>store(creditcard, options = {})</tt>
|
||||
# * <tt>unstore(identification, options = {})</tt>
|
||||
#
|
||||
# === Gateway Options
|
||||
# The options hash consists of the following options:
|
||||
#
|
||||
# * <tt>:order_id</tt> - The order number
|
||||
# * <tt>:ip</tt> - The IP address of the customer making the purchase
|
||||
# * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
|
||||
# * <tt>:invoice</tt> - The invoice number
|
||||
# * <tt>:merchant</tt> - The name or description of the merchant offering the product
|
||||
# * <tt>:description</tt> - A description of the transaction
|
||||
# * <tt>:email</tt> - The email address of the customer
|
||||
# * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
|
||||
# * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
|
||||
# * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
|
||||
#
|
||||
# The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
|
||||
#
|
||||
# * <tt>:name</tt> - The full name of the customer.
|
||||
# * <tt>:company</tt> - The company name of the customer.
|
||||
# * <tt>:address1</tt> - The primary street address of the customer.
|
||||
# * <tt>:address2</tt> - Additional line of address information.
|
||||
# * <tt>:city</tt> - The city of the customer.
|
||||
# * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
|
||||
# * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
|
||||
# * <tt>:zip</tt> - The zip or postal code of the customer.
|
||||
# * <tt>:phone</tt> - The phone number of the customer.
|
||||
#
|
||||
# == Implmenting new gateways
|
||||
#
|
||||
# See the {ActiveMerchant Guide to Contributing}[https://github.com/Shopify/active_merchant/wiki/Contributing]
|
||||
#
|
||||
class Gateway
|
||||
include PostsData
|
||||
include RequiresParameters
|
||||
include CreditCardFormatting
|
||||
include Utils
|
||||
|
||||
DEBIT_CARDS = [ :switch, :solo ]
|
||||
CURRENCIES_WITHOUT_FRACTIONS = [ 'JPY', 'HUF', 'TWD' ]
|
||||
CREDIT_DEPRECATION_MESSAGE = "Support for using credit to refund existing transactions is deprecated and will be removed from a future release of ActiveMerchant. Please use the refund method instead."
|
||||
|
||||
cattr_reader :implementations
|
||||
@@implementations = []
|
||||
|
||||
def self.inherited(subclass)
|
||||
super
|
||||
@@implementations << subclass
|
||||
end
|
||||
|
||||
# The format of the amounts used by the gateway
|
||||
# :dollars => '12.50'
|
||||
# :cents => '1250'
|
||||
class_attribute :money_format
|
||||
self.money_format = :dollars
|
||||
|
||||
# The default currency for the transactions if no currency is provided
|
||||
class_attribute :default_currency
|
||||
|
||||
# The countries of merchants the gateway supports
|
||||
class_attribute :supported_countries
|
||||
self.supported_countries = []
|
||||
|
||||
# The supported card types for the gateway
|
||||
class_attribute :supported_cardtypes
|
||||
self.supported_cardtypes = []
|
||||
|
||||
class_attribute :homepage_url
|
||||
class_attribute :display_name
|
||||
|
||||
class_attribute :test_url, :live_url
|
||||
|
||||
class_attribute :abstract_class
|
||||
|
||||
self.abstract_class = false
|
||||
|
||||
# The application making the calls to the gateway
|
||||
# Useful for things like the PayPal build notation (BN) id fields
|
||||
superclass_delegating_accessor :application_id
|
||||
self.application_id = 'ActiveMerchant'
|
||||
|
||||
attr_reader :options
|
||||
|
||||
# Use this method to check if your gateway of interest supports a credit card of some type
|
||||
def self.supports?(card_type)
|
||||
supported_cardtypes.include?(card_type.to_sym)
|
||||
end
|
||||
|
||||
def self.card_brand(source)
|
||||
result = source.respond_to?(:brand) ? source.brand : source.type
|
||||
result.to_s.downcase
|
||||
end
|
||||
|
||||
def card_brand(source)
|
||||
self.class.card_brand(source)
|
||||
end
|
||||
|
||||
# Initialize a new gateway.
|
||||
#
|
||||
# See the documentation for the gateway you will be using to make sure there are no other
|
||||
# required options.
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
end
|
||||
|
||||
# Are we running in test mode?
|
||||
def test?
|
||||
(@options.has_key?(:test) ? @options[:test] : Base.test?)
|
||||
end
|
||||
|
||||
private # :nodoc: all
|
||||
|
||||
def name
|
||||
self.class.name.scan(/\:\:(\w+)Gateway/).flatten.first
|
||||
end
|
||||
|
||||
def amount(money)
|
||||
return nil if money.nil?
|
||||
cents = if money.respond_to?(:cents)
|
||||
deprecated "Support for Money objects is deprecated and will be removed from a future release of ActiveMerchant. Please use an Integer value in cents"
|
||||
money.cents
|
||||
else
|
||||
money
|
||||
end
|
||||
|
||||
if money.is_a?(String)
|
||||
raise ArgumentError, 'money amount must be a positive Integer in cents.'
|
||||
end
|
||||
|
||||
if self.money_format == :cents
|
||||
cents.to_s
|
||||
else
|
||||
sprintf("%.2f", cents.to_f / 100)
|
||||
end
|
||||
end
|
||||
|
||||
def localized_amount(money, currency)
|
||||
amount = amount(money)
|
||||
CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
|
||||
end
|
||||
|
||||
def currency(money)
|
||||
money.respond_to?(:currency) ? money.currency : self.default_currency
|
||||
end
|
||||
|
||||
def requires_start_date_or_issue_number?(credit_card)
|
||||
return false if card_brand(credit_card).blank?
|
||||
DEBIT_CARDS.include?(card_brand(credit_card).to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
17
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways.rb
vendored
Normal file
17
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways.rb
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
module ActiveMerchant
|
||||
module Billing
|
||||
autoload :Gateway, 'active_merchant/billing/gateway'
|
||||
|
||||
Dir[File.dirname(__FILE__) + '/gateways/**/*.rb'].each do |f|
|
||||
# Get camelized class name
|
||||
filename = File.basename(f, '.rb')
|
||||
# Add _gateway suffix
|
||||
gateway_name = filename + '_gateway'
|
||||
# Camelize the string to get the class name
|
||||
gateway_class = gateway_name.camelize
|
||||
|
||||
# Register for autoloading
|
||||
autoload gateway_class, f
|
||||
end
|
||||
end
|
||||
end
|
||||
724
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/authorize_net.rb
vendored
Normal file
724
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/authorize_net.rb
vendored
Normal file
@@ -0,0 +1,724 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# For more information on the Authorize.Net Gateway please visit their {Integration Center}[http://developer.authorize.net/]
|
||||
#
|
||||
# The login and password are not the username and password you use to
|
||||
# login to the Authorize.Net Merchant Interface. Instead, you will
|
||||
# use the API Login ID as the login and Transaction Key as the
|
||||
# password.
|
||||
#
|
||||
# ==== How to Get Your API Login ID and Transaction Key
|
||||
#
|
||||
# 1. Log into the Merchant Interface
|
||||
# 2. Select Settings from the Main Menu
|
||||
# 3. Click on API Login ID and Transaction Key in the Security section
|
||||
# 4. Type in the answer to the secret question configured on setup
|
||||
# 5. Click Submit
|
||||
#
|
||||
# ==== Automated Recurring Billing (ARB)
|
||||
#
|
||||
# Automated Recurring Billing (ARB) is an optional service for submitting and managing recurring, or subscription-based, transactions.
|
||||
#
|
||||
# To use recurring, update_recurring, cancel_recurring and status_recurring ARB must be enabled for your account.
|
||||
#
|
||||
# Information about ARB is available on the {Authorize.Net website}[http://www.authorize.net/solutions/merchantsolutions/merchantservices/automatedrecurringbilling/].
|
||||
# Information about the ARB API is available at the {Authorize.Net Integration Center}[http://developer.authorize.net/]
|
||||
class AuthorizeNetGateway < Gateway
|
||||
API_VERSION = '3.1'
|
||||
|
||||
class_attribute :arb_test_url, :arb_live_url
|
||||
|
||||
self.test_url = "https://test.authorize.net/gateway/transact.dll"
|
||||
self.live_url = "https://secure.authorize.net/gateway/transact.dll"
|
||||
|
||||
self.arb_test_url = 'https://apitest.authorize.net/xml/v1/request.api'
|
||||
self.arb_live_url = 'https://api.authorize.net/xml/v1/request.api'
|
||||
|
||||
class_attribute :duplicate_window
|
||||
|
||||
APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
|
||||
|
||||
RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
|
||||
AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
|
||||
|
||||
self.default_currency = 'USD'
|
||||
|
||||
self.supported_countries = ['US', 'CA', 'GB']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
||||
self.homepage_url = 'http://www.authorize.net/'
|
||||
self.display_name = 'Authorize.Net'
|
||||
|
||||
CARD_CODE_ERRORS = %w( N S )
|
||||
AVS_ERRORS = %w( A E N R W Z )
|
||||
AVS_REASON_CODES = %w(27 45)
|
||||
|
||||
AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
|
||||
|
||||
RECURRING_ACTIONS = {
|
||||
:create => 'ARBCreateSubscription',
|
||||
:update => 'ARBUpdateSubscription',
|
||||
:cancel => 'ARBCancelSubscription',
|
||||
:status => 'ARBGetSubscriptionStatus'
|
||||
}
|
||||
|
||||
# Creates a new AuthorizeNetGateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
|
||||
# * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
|
||||
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
||||
# Otherwise, perform transactions against the production server.
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
||||
# charge the card.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>paysource</tt> -- The CreditCard or Check details for the transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
def authorize(money, paysource, options = {})
|
||||
post = {}
|
||||
add_currency_code(post, money, options)
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, paysource, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
add_duplicate_window(post)
|
||||
|
||||
commit('AUTH_ONLY', money, post)
|
||||
end
|
||||
|
||||
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
||||
# * <tt>paysource</tt> -- The CreditCard or Check details for the transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
def purchase(money, paysource, options = {})
|
||||
post = {}
|
||||
add_currency_code(post, money, options)
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, paysource, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
add_duplicate_window(post)
|
||||
|
||||
commit('AUTH_CAPTURE', money, post)
|
||||
end
|
||||
|
||||
# Captures the funds from an authorized transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
||||
# * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
|
||||
def capture(money, authorization, options = {})
|
||||
post = {:trans_id => authorization}
|
||||
add_customer_data(post, options)
|
||||
add_invoice(post, options)
|
||||
commit('PRIOR_AUTH_CAPTURE', money, post)
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous authorize request.
|
||||
def void(authorization, options = {})
|
||||
post = {:trans_id => authorization}
|
||||
add_duplicate_window(post)
|
||||
commit('VOID', nil, post)
|
||||
end
|
||||
|
||||
# Refund a transaction.
|
||||
#
|
||||
# This transaction indicates to the gateway that
|
||||
# money should flow from the merchant to the customer.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
||||
# * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:card_number</tt> -- The credit card number the refund is being issued to. (REQUIRED)
|
||||
# You can either pass the last four digits of the card number or the full card number.
|
||||
# * <tt>:first_name</tt> -- The first name of the account being refunded.
|
||||
# * <tt>:last_name</tt> -- The last name of the account being refunded.
|
||||
# * <tt>:zip</tt> -- The postal code of the account being refunded.
|
||||
def refund(money, identification, options = {})
|
||||
requires!(options, :card_number)
|
||||
|
||||
post = { :trans_id => identification,
|
||||
:card_num => options[:card_number]
|
||||
}
|
||||
|
||||
post[:first_name] = options[:first_name] if options[:first_name]
|
||||
post[:last_name] = options[:last_name] if options[:last_name]
|
||||
post[:zip] = options[:zip] if options[:zip]
|
||||
|
||||
add_invoice(post, options)
|
||||
add_duplicate_window(post)
|
||||
|
||||
commit('CREDIT', money, post)
|
||||
end
|
||||
|
||||
def credit(money, identification, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, identification, options)
|
||||
end
|
||||
|
||||
# Create a recurring payment.
|
||||
#
|
||||
# This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be charged to the customer at each interval as an Integer value in cents.
|
||||
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:interval</tt> -- A hash containing information about the interval of time between payments. Must
|
||||
# contain the keys <tt>:length</tt> and <tt>:unit</tt>. <tt>:unit</tt> can be either <tt>:months</tt> or <tt>:days</tt>.
|
||||
# If <tt>:unit</tt> is <tt>:months</tt> then <tt>:length</tt> must be an integer between 1 and 12 inclusive.
|
||||
# If <tt>:unit</tt> is <tt>:days</tt> then <tt>:length</tt> must be an integer between 7 and 365 inclusive.
|
||||
# For example, to charge the customer once every three months the hash would be
|
||||
# +:interval => { :unit => :months, :length => 3 }+ (REQUIRED)
|
||||
# * <tt>:duration</tt> -- A hash containing keys for the <tt>:start_date</tt> the subscription begins (also the date the
|
||||
# initial billing occurs) and the total number of billing <tt>:occurences</tt> or payments for the subscription. (REQUIRED)
|
||||
def recurring(money, creditcard, options={})
|
||||
requires!(options, :interval, :duration, :billing_address)
|
||||
requires!(options[:interval], :length, [:unit, :days, :months])
|
||||
requires!(options[:duration], :start_date, :occurrences)
|
||||
requires!(options[:billing_address], :first_name, :last_name)
|
||||
|
||||
options[:credit_card] = creditcard
|
||||
options[:amount] = money
|
||||
|
||||
request = build_recurring_request(:create, options)
|
||||
recurring_commit(:create, request)
|
||||
end
|
||||
|
||||
# Update a recurring payment's details.
|
||||
#
|
||||
# This transaction updates an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
||||
# and the subscription must have already been created previously by calling +recurring()+. The ability to change certain
|
||||
# details about a recurring payment is dependent on transaction history and cannot be determined until after calling
|
||||
# +update_recurring()+. See the ARB XML Guide for such conditions.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:subscription_id</tt> -- A string containing the <tt>:subscription_id</tt> of the recurring payment already in place
|
||||
# for a given credit card. (REQUIRED)
|
||||
def update_recurring(options={})
|
||||
requires!(options, :subscription_id)
|
||||
request = build_recurring_request(:update, options)
|
||||
recurring_commit(:update, request)
|
||||
end
|
||||
|
||||
# Cancel a recurring payment.
|
||||
#
|
||||
# This transaction cancels an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
|
||||
# and the subscription must have already been created previously by calling recurring()
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
|
||||
# for a given credit card. (REQUIRED)
|
||||
def cancel_recurring(subscription_id)
|
||||
request = build_recurring_request(:cancel, :subscription_id => subscription_id)
|
||||
recurring_commit(:cancel, request)
|
||||
end
|
||||
|
||||
# Get Subscription Status of a recurring payment.
|
||||
#
|
||||
# This transaction gets the status of an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
|
||||
# for a given credit card. (REQUIRED)
|
||||
def status_recurring(subscription_id)
|
||||
request = build_recurring_request(:status, :subscription_id => subscription_id)
|
||||
recurring_commit(:status, request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit(action, money, parameters)
|
||||
parameters[:amount] = amount(money) unless action == 'VOID'
|
||||
|
||||
# Only activate the test_request when the :test option is passed in
|
||||
parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
|
||||
|
||||
url = test? ? self.test_url : self.live_url
|
||||
data = ssl_post url, post_data(action, parameters)
|
||||
|
||||
response = parse(data)
|
||||
response[:action] = action
|
||||
|
||||
message = message_from(response)
|
||||
|
||||
# Return the response. The authorization can be taken out of the transaction_id
|
||||
# Test Mode on/off is something we have to parse from the response text.
|
||||
# It usually looks something like this
|
||||
#
|
||||
# (TESTMODE) Successful Sale
|
||||
test_mode = test? || message =~ /TESTMODE/
|
||||
|
||||
Response.new(success?(response), message, response,
|
||||
:test => test_mode,
|
||||
:authorization => response[:transaction_id],
|
||||
:fraud_review => fraud_review?(response),
|
||||
:avs_result => { :code => response[:avs_result_code] },
|
||||
:cvv_result => response[:card_code]
|
||||
)
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response[:response_code] == APPROVED
|
||||
end
|
||||
|
||||
def fraud_review?(response)
|
||||
response[:response_code] == FRAUD_REVIEW
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
fields = split(body)
|
||||
|
||||
results = {
|
||||
:response_code => fields[RESPONSE_CODE].to_i,
|
||||
:response_reason_code => fields[RESPONSE_REASON_CODE],
|
||||
:response_reason_text => fields[RESPONSE_REASON_TEXT],
|
||||
:avs_result_code => fields[AVS_RESULT_CODE],
|
||||
:transaction_id => fields[TRANSACTION_ID],
|
||||
:card_code => fields[CARD_CODE_RESPONSE_CODE]
|
||||
}
|
||||
results
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {}
|
||||
|
||||
post[:version] = API_VERSION
|
||||
post[:login] = @options[:login]
|
||||
post[:tran_key] = @options[:password]
|
||||
post[:relay_response] = "FALSE"
|
||||
post[:type] = action
|
||||
post[:delim_data] = "TRUE"
|
||||
post[:delim_char] = ","
|
||||
post[:encap_char] = "$"
|
||||
post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
|
||||
|
||||
request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
request
|
||||
end
|
||||
|
||||
def add_currency_code(post, money, options)
|
||||
post[:currency_code] = options[:currency] || currency(money)
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:invoice_num] = options[:order_id]
|
||||
post[:description] = options[:description]
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:card_num] = creditcard.number
|
||||
post[:card_code] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:exp_date] = expdate(creditcard)
|
||||
post[:first_name] = creditcard.first_name
|
||||
post[:last_name] = creditcard.last_name
|
||||
end
|
||||
|
||||
def add_payment_source(params, source, options={})
|
||||
if card_brand(source) == "check"
|
||||
add_check(params, source, options)
|
||||
else
|
||||
add_creditcard(params, source)
|
||||
end
|
||||
end
|
||||
|
||||
def add_check(post, check, options)
|
||||
post[:method] = "ECHECK"
|
||||
post[:bank_name] = check.bank_name
|
||||
post[:bank_aba_code] = check.routing_number
|
||||
post[:bank_acct_num] = check.account_number
|
||||
post[:bank_acct_type] = check.account_type
|
||||
post[:echeck_type] = "WEB"
|
||||
post[:bank_acct_name] = check.name
|
||||
post[:bank_check_number] = check.number if check.number.present?
|
||||
post[:recurring_billing] = (options[:recurring] ? "TRUE" : "FALSE")
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
if options.has_key? :email
|
||||
post[:email] = options[:email]
|
||||
post[:email_customer] = false
|
||||
end
|
||||
|
||||
if options.has_key? :customer
|
||||
post[:cust_id] = options[:customer] if Float(options[:customer]) rescue nil
|
||||
end
|
||||
|
||||
if options.has_key? :ip
|
||||
post[:customer_ip] = options[:ip]
|
||||
end
|
||||
end
|
||||
|
||||
# x_duplicate_window won't be sent by default, because sending it changes the response.
|
||||
# "If this field is present in the request with or without a value, an enhanced duplicate transaction response will be sent."
|
||||
# (as of 2008-12-30) http://www.authorize.net/support/AIM_guide_SCC.pdf
|
||||
def add_duplicate_window(post)
|
||||
unless duplicate_window.nil?
|
||||
post[:duplicate_window] = duplicate_window
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:address] = address[:address1].to_s
|
||||
post[:company] = address[:company].to_s
|
||||
post[:phone] = address[:phone].to_s
|
||||
post[:zip] = address[:zip].to_s
|
||||
post[:city] = address[:city].to_s
|
||||
post[:country] = address[:country].to_s
|
||||
post[:state] = address[:state].blank? ? 'n/a' : address[:state]
|
||||
end
|
||||
|
||||
if address = options[:shipping_address]
|
||||
post[:ship_to_first_name] = address[:first_name].to_s
|
||||
post[:ship_to_last_name] = address[:last_name].to_s
|
||||
post[:ship_to_address] = address[:address1].to_s
|
||||
post[:ship_to_company] = address[:company].to_s
|
||||
post[:ship_to_phone] = address[:phone].to_s
|
||||
post[:ship_to_zip] = address[:zip].to_s
|
||||
post[:ship_to_city] = address[:city].to_s
|
||||
post[:ship_to_country] = address[:country].to_s
|
||||
post[:ship_to_state] = address[:state].blank? ? 'n/a' : address[:state]
|
||||
end
|
||||
end
|
||||
|
||||
# Make a ruby type out of the response string
|
||||
def normalize(field)
|
||||
case field
|
||||
when "true" then true
|
||||
when "false" then false
|
||||
when "" then nil
|
||||
when "null" then nil
|
||||
else field
|
||||
end
|
||||
end
|
||||
|
||||
def message_from(results)
|
||||
if results[:response_code] == DECLINED
|
||||
return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
|
||||
if AVS_REASON_CODES.include?(results[:response_reason_code]) && AVS_ERRORS.include?(results[:avs_result_code])
|
||||
return AVSResult.messages[ results[:avs_result_code] ]
|
||||
end
|
||||
end
|
||||
|
||||
(results[:response_reason_text] ? results[:response_reason_text].chomp('.') : '')
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = sprintf("%.4i", creditcard.year)
|
||||
month = sprintf("%.2i", creditcard.month)
|
||||
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def split(response)
|
||||
response[1..-2].split(/\$,\$/)
|
||||
end
|
||||
|
||||
# ARB
|
||||
|
||||
# Builds recurring billing request
|
||||
def build_recurring_request(action, options = {})
|
||||
unless RECURRING_ACTIONS.include?(action)
|
||||
raise StandardError, "Invalid Automated Recurring Billing Action: #{action}"
|
||||
end
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
||||
xml.tag!("#{RECURRING_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_ARB_NAMESPACE) do
|
||||
add_arb_merchant_authentication(xml)
|
||||
# Merchant-assigned reference ID for the request
|
||||
xml.tag!('refId', options[:ref_id]) if options[:ref_id]
|
||||
send("build_arb_#{action}_subscription_request", xml, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Contains the merchant’s payment gateway account authentication information
|
||||
def add_arb_merchant_authentication(xml)
|
||||
xml.tag!('merchantAuthentication') do
|
||||
xml.tag!('name', @options[:login])
|
||||
xml.tag!('transactionKey', @options[:password])
|
||||
end
|
||||
end
|
||||
|
||||
# Builds body for ARBCreateSubscriptionRequest
|
||||
def build_arb_create_subscription_request(xml, options)
|
||||
# Subscription
|
||||
add_arb_subscription(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Builds body for ARBUpdateSubscriptionRequest
|
||||
def build_arb_update_subscription_request(xml, options)
|
||||
xml.tag!('subscriptionId', options[:subscription_id])
|
||||
# Adds Subscription
|
||||
add_arb_subscription(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Builds body for ARBCancelSubscriptionRequest
|
||||
def build_arb_cancel_subscription_request(xml, options)
|
||||
xml.tag!('subscriptionId', options[:subscription_id])
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Builds body for ARBGetSubscriptionStatusRequest
|
||||
def build_arb_status_subscription_request(xml, options)
|
||||
xml.tag!('subscriptionId', options[:subscription_id])
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Adds subscription information
|
||||
def add_arb_subscription(xml, options)
|
||||
xml.tag!('subscription') do
|
||||
# Merchant-assigned name for the subscription (optional)
|
||||
xml.tag!('name', options[:subscription_name]) if options[:subscription_name]
|
||||
# Contains information about the payment schedule
|
||||
add_arb_payment_schedule(xml, options)
|
||||
# The amount to be billed to the customer
|
||||
# for each payment in the subscription
|
||||
xml.tag!('amount', amount(options[:amount])) if options[:amount]
|
||||
if trial = options[:trial]
|
||||
# The amount to be charged for each payment during a trial period (conditional)
|
||||
xml.tag!('trialAmount', amount(trial[:amount])) if trial[:amount]
|
||||
end
|
||||
# Contains either the customer’s credit card
|
||||
# or bank account payment information
|
||||
add_arb_payment(xml, options)
|
||||
# Contains order information (optional)
|
||||
add_arb_order(xml, options)
|
||||
# Contains information about the customer
|
||||
add_arb_customer(xml, options)
|
||||
# Contains the customer's billing address information
|
||||
add_arb_address(xml, 'billTo', options[:billing_address])
|
||||
# Contains the customer's shipping address information (optional)
|
||||
add_arb_address(xml, 'shipTo', options[:shipping_address])
|
||||
end
|
||||
end
|
||||
|
||||
# Adds information about the interval of time between payments
|
||||
def add_arb_interval(xml, options)
|
||||
interval = options[:interval]
|
||||
return unless interval
|
||||
xml.tag!('interval') do
|
||||
# The measurement of time, in association with the Interval Unit,
|
||||
# that is used to define the frequency of the billing occurrences
|
||||
xml.tag!('length', interval[:length])
|
||||
# The unit of time, in association with the Interval Length,
|
||||
# between each billing occurrence
|
||||
xml.tag!('unit', interval[:unit].to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Adds information about the subscription duration
|
||||
def add_arb_duration(xml, options)
|
||||
duration = options[:duration]
|
||||
return unless duration
|
||||
# The date the subscription begins
|
||||
# (also the date the initial billing occurs)
|
||||
xml.tag!('startDate', duration[:start_date]) if duration[:start_date]
|
||||
# Number of billing occurrences or payments for the subscription
|
||||
xml.tag!('totalOccurrences', duration[:occurrences]) if duration[:occurrences]
|
||||
end
|
||||
|
||||
def add_arb_payment_schedule(xml, options)
|
||||
return unless options[:interval] || options[:duration]
|
||||
xml.tag!('paymentSchedule') do
|
||||
# Contains information about the interval of time between payments
|
||||
add_arb_interval(xml, options)
|
||||
add_arb_duration(xml, options)
|
||||
if trial = options[:trial]
|
||||
# Number of billing occurrences or payments in the trial period (optional)
|
||||
xml.tag!('trialOccurrences', trial[:occurrences]) if trial[:occurrences]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Adds customer's credit card or bank account payment information
|
||||
def add_arb_payment(xml, options)
|
||||
return unless options[:credit_card] || options[:bank_account]
|
||||
xml.tag!('payment') do
|
||||
# Contains the customer’s credit card information
|
||||
add_arb_credit_card(xml, options)
|
||||
# Contains the customer’s bank account information
|
||||
add_arb_bank_account(xml, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Adds customer’s credit card information
|
||||
# Note: This element should only be included
|
||||
# when the payment method is credit card.
|
||||
def add_arb_credit_card(xml, options)
|
||||
credit_card = options[:credit_card]
|
||||
return unless credit_card
|
||||
xml.tag!('creditCard') do
|
||||
# The credit card number used for payment of the subscription
|
||||
xml.tag!('cardNumber', credit_card.number)
|
||||
# The expiration date of the credit card used for the subscription
|
||||
xml.tag!('expirationDate', arb_expdate(credit_card))
|
||||
end
|
||||
end
|
||||
|
||||
# Adds customer’s bank account information
|
||||
# Note: This element should only be included
|
||||
# when the payment method is bank account.
|
||||
def add_arb_bank_account(xml, options)
|
||||
bank_account = options[:bank_account]
|
||||
return unless bank_account
|
||||
xml.tag!('bankAccount') do
|
||||
# The type of bank account used for payment of the subscription
|
||||
xml.tag!('accountType', bank_account[:account_type])
|
||||
# The routing number of the customer’s bank
|
||||
xml.tag!('routingNumber', bank_account[:routing_number])
|
||||
# The bank account number used for payment of the subscription
|
||||
xml.tag!('accountNumber', bank_account[:account_number])
|
||||
# The full name of the individual associated
|
||||
# with the bank account number
|
||||
xml.tag!('nameOfAccount', bank_account[:name_of_account])
|
||||
# The full name of the individual associated
|
||||
# with the bank account number (optional)
|
||||
xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
|
||||
# The type of electronic check transaction used for the subscription
|
||||
xml.tag!('echeckType', bank_account[:echeck_type])
|
||||
end
|
||||
end
|
||||
|
||||
# Adds order information (optional)
|
||||
def add_arb_order(xml, options)
|
||||
order = options[:order]
|
||||
return unless order
|
||||
xml.tag!('order') do
|
||||
# Merchant-assigned invoice number for the subscription (optional)
|
||||
xml.tag!('invoiceNumber', order[:invoice_number])
|
||||
# Description of the subscription (optional)
|
||||
xml.tag!('description', order[:description])
|
||||
end
|
||||
end
|
||||
|
||||
# Adds information about the customer
|
||||
def add_arb_customer(xml, options)
|
||||
customer = options[:customer]
|
||||
return unless customer
|
||||
xml.tag!('customer') do
|
||||
xml.tag!('type', customer[:type]) if customer[:type]
|
||||
xml.tag!('id', customer[:id]) if customer[:id]
|
||||
xml.tag!('email', customer[:email]) if customer[:email]
|
||||
xml.tag!('phoneNumber', customer[:phone_number]) if customer[:phone_number]
|
||||
xml.tag!('faxNumber', customer[:fax_number]) if customer[:fax_number]
|
||||
add_arb_drivers_license(xml, options)
|
||||
xml.tag!('taxId', customer[:tax_id]) if customer[:tax_id]
|
||||
end
|
||||
end
|
||||
|
||||
# Adds the customer's driver's license information (conditional)
|
||||
def add_arb_drivers_license(xml, options)
|
||||
return unless customer = options[:customer]
|
||||
return unless drivers_license = customer[:drivers_license]
|
||||
xml.tag!('driversLicense') do
|
||||
# The customer's driver's license number
|
||||
xml.tag!('number', drivers_license[:number])
|
||||
# The customer's driver's license state
|
||||
xml.tag!('state', drivers_license[:state])
|
||||
# The customer's driver's license date of birth
|
||||
xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
|
||||
end
|
||||
end
|
||||
|
||||
# Adds address information
|
||||
def add_arb_address(xml, container_name, address)
|
||||
return if address.blank?
|
||||
xml.tag!(container_name) do
|
||||
xml.tag!('firstName', address[:first_name])
|
||||
xml.tag!('lastName', address[:last_name])
|
||||
xml.tag!('company', address[:company])
|
||||
xml.tag!('address', address[:address1])
|
||||
xml.tag!('city', address[:city])
|
||||
xml.tag!('state', address[:state])
|
||||
xml.tag!('zip', address[:zip])
|
||||
xml.tag!('country', address[:country])
|
||||
end
|
||||
end
|
||||
|
||||
def arb_expdate(credit_card)
|
||||
sprintf('%04d-%02d', credit_card.year, credit_card.month)
|
||||
end
|
||||
|
||||
def recurring_commit(action, request)
|
||||
url = test? ? arb_test_url : arb_live_url
|
||||
xml = ssl_post(url, request, "Content-Type" => "text/xml")
|
||||
|
||||
response = recurring_parse(action, xml)
|
||||
|
||||
message = response[:message] || response[:text]
|
||||
test_mode = test? || message =~ /Test Mode/
|
||||
success = response[:result_code] == 'Ok'
|
||||
|
||||
Response.new(success, message, response,
|
||||
:test => test_mode,
|
||||
:authorization => response[:subscription_id]
|
||||
)
|
||||
end
|
||||
|
||||
def recurring_parse(action, xml)
|
||||
response = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
root = REXML::XPath.first(xml, "//#{RECURRING_ACTIONS[action]}Response") ||
|
||||
REXML::XPath.first(xml, "//ErrorResponse")
|
||||
if root
|
||||
root.elements.to_a.each do |node|
|
||||
recurring_parse_element(response, node)
|
||||
end
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def recurring_parse_element(response, node)
|
||||
if node.has_elements?
|
||||
node.elements.each{|e| recurring_parse_element(response, e) }
|
||||
else
|
||||
response[node.name.underscore.to_sym] = node.text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
956
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/authorize_net_cim.rb
vendored
Normal file
956
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/authorize_net_cim.rb
vendored
Normal file
@@ -0,0 +1,956 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# ==== Customer Information Manager (CIM)
|
||||
#
|
||||
# The Authorize.Net Customer Information Manager (CIM) is an optional additional service that allows you to store sensitive payment information on
|
||||
# Authorize.Net's servers, simplifying payments for returning customers and recurring transactions. It can also help with Payment Card Industry (PCI)
|
||||
# Data Security Standard compliance, since customer data is no longer stored locally.
|
||||
#
|
||||
# To use the AuthorizeNetCimGateway CIM must be enabled for your account.
|
||||
#
|
||||
# Information about CIM is available on the {Authorize.Net website}[http://www.authorize.net/solutions/merchantsolutions/merchantservices/cim/].
|
||||
# Information about the CIM API is available at the {Authorize.Net Integration Center}[http://developer.authorize.net/]
|
||||
#
|
||||
# ==== Login and Password
|
||||
#
|
||||
# The login and password are not the username and password you use to
|
||||
# login to the Authorize.Net Merchant Interface. Instead, you will
|
||||
# use the API Login ID as the login and Transaction Key as the
|
||||
# password.
|
||||
#
|
||||
# ==== How to Get Your API Login ID and Transaction Key
|
||||
#
|
||||
# 1. Log into the Merchant Interface
|
||||
# 2. Select Settings from the Main Menu
|
||||
# 3. Click on API Login ID and Transaction Key in the Security section
|
||||
# 4. Type in the answer to the secret question configured on setup
|
||||
# 5. Click Submit
|
||||
class AuthorizeNetCimGateway < Gateway
|
||||
self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
|
||||
self.live_url = 'https://api.authorize.net/xml/v1/request.api'
|
||||
|
||||
AUTHORIZE_NET_CIM_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
|
||||
|
||||
CIM_ACTIONS = {
|
||||
:create_customer_profile => 'createCustomerProfile',
|
||||
:create_customer_payment_profile => 'createCustomerPaymentProfile',
|
||||
:create_customer_shipping_address => 'createCustomerShippingAddress',
|
||||
:get_customer_profile => 'getCustomerProfile',
|
||||
:get_customer_profile_ids => 'getCustomerProfileIds',
|
||||
:get_customer_payment_profile => 'getCustomerPaymentProfile',
|
||||
:get_customer_shipping_address => 'getCustomerShippingAddress',
|
||||
:delete_customer_profile => 'deleteCustomerProfile',
|
||||
:delete_customer_payment_profile => 'deleteCustomerPaymentProfile',
|
||||
:delete_customer_shipping_address => 'deleteCustomerShippingAddress',
|
||||
:update_customer_profile => 'updateCustomerProfile',
|
||||
:update_customer_payment_profile => 'updateCustomerPaymentProfile',
|
||||
:update_customer_shipping_address => 'updateCustomerShippingAddress',
|
||||
:create_customer_profile_transaction => 'createCustomerProfileTransaction',
|
||||
:validate_customer_payment_profile => 'validateCustomerPaymentProfile'
|
||||
}
|
||||
|
||||
CIM_TRANSACTION_TYPES = {
|
||||
:auth_capture => 'profileTransAuthCapture',
|
||||
:auth_only => 'profileTransAuthOnly',
|
||||
:capture_only => 'profileTransCaptureOnly',
|
||||
:prior_auth_capture => 'profileTransPriorAuthCapture',
|
||||
:refund => 'profileTransRefund',
|
||||
:void => 'profileTransVoid'
|
||||
}
|
||||
|
||||
CIM_VALIDATION_MODES = {
|
||||
:none => 'none',
|
||||
:test => 'testMode',
|
||||
:live => 'liveMode',
|
||||
:old => 'oldLiveMode'
|
||||
}
|
||||
|
||||
BANK_ACCOUNT_TYPES = {
|
||||
:checking => 'checking',
|
||||
:savings => 'savings',
|
||||
:business_checking => 'businessChecking'
|
||||
}
|
||||
|
||||
ECHECK_TYPES = {
|
||||
:ccd => 'CCD',
|
||||
:ppd => 'PPD',
|
||||
:web => 'WEB'
|
||||
}
|
||||
|
||||
self.homepage_url = 'http://www.authorize.net/'
|
||||
self.display_name = 'Authorize.Net CIM'
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# Creates a new AuthorizeNetCimGateway
|
||||
#
|
||||
# The gateway requires that a valid API Login ID and Transaction Key be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
|
||||
# * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
|
||||
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
||||
# Otherwise, perform transactions against the production server.
|
||||
# * <tt>:delimiter</tt> -- The delimiter used in the direct response. Default is ',' (comma).
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Creates a new customer profile along with any customer payment profiles and customer shipping addresses
|
||||
# for the customer profile.
|
||||
#
|
||||
# Returns a Response with the Customer Profile ID of the new customer profile in the authorization field.
|
||||
# It is *CRITICAL* that you save this ID. There is no way to retrieve this through the API. You will not
|
||||
# be able to create another Customer Profile with the same information.
|
||||
#
|
||||
#
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:profile</tt> -- A hash containing at least one of the CONDITIONAL profile options below (REQUIRED)
|
||||
#
|
||||
# ==== Profile
|
||||
#
|
||||
# * <tt>:email</tt> -- Email address associated with the customer profile (CONDITIONAL)
|
||||
# * <tt>:description</tt> -- Description of the customer or customer profile (CONDITIONAL)
|
||||
# * <tt>:merchant_customer_id</tt> -- Merchant assigned ID for the customer (CONDITIONAL)
|
||||
# * <tt>:payment_profile</tt> -- A hash containing the elements of the new payment profile (optional)
|
||||
#
|
||||
# ==== Payment Profile
|
||||
#
|
||||
# * <tt>:payment</tt> -- A hash containing information on payment. Either :credit_card or :bank_account (optional)
|
||||
def create_customer_profile(options)
|
||||
requires!(options, :profile)
|
||||
requires!(options[:profile], :email) unless options[:profile][:merchant_customer_id] || options[:profile][:description]
|
||||
requires!(options[:profile], :description) unless options[:profile][:email] || options[:profile][:merchant_customer_id]
|
||||
requires!(options[:profile], :merchant_customer_id) unless options[:profile][:description] || options[:profile][:email]
|
||||
|
||||
request = build_request(:create_customer_profile, options)
|
||||
commit(:create_customer_profile, request)
|
||||
end
|
||||
|
||||
# Creates a new customer payment profile for an existing customer profile.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer the payment profile will be added to. (REQUIRED)
|
||||
# * <tt>:payment_profile</tt> -- A hash containing the elements of the new payment profile (REQUIRED)
|
||||
#
|
||||
# ==== Payment Profile
|
||||
#
|
||||
# * <tt>:payment</tt> -- A hash containing information on payment. Either :credit_card or :bank_account (REQUIRED)
|
||||
def create_customer_payment_profile(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :payment_profile)
|
||||
requires!(options[:payment_profile], :payment)
|
||||
|
||||
request = build_request(:create_customer_payment_profile, options)
|
||||
commit(:create_customer_payment_profile, request)
|
||||
end
|
||||
|
||||
# Creates a new customer shipping address for an existing customer profile.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer the payment profile will be added to. (REQUIRED)
|
||||
# * <tt>:address</tt> -- A hash containing the elements of the shipping address (REQUIRED)
|
||||
def create_customer_shipping_address(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :address)
|
||||
|
||||
request = build_request(:create_customer_shipping_address, options)
|
||||
commit(:create_customer_shipping_address, request)
|
||||
end
|
||||
|
||||
# Deletes an existing customer profile along with all associated customer payment profiles and customer shipping addresses.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to be deleted. (REQUIRED)
|
||||
def delete_customer_profile(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
|
||||
request = build_request(:delete_customer_profile, options)
|
||||
commit(:delete_customer_profile, request)
|
||||
end
|
||||
|
||||
# Deletes a customer payment profile from an existing customer profile.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be deleted. (REQUIRED)
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Payment Profile ID of the payment profile to be deleted. (REQUIRED)
|
||||
def delete_customer_payment_profile(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :customer_payment_profile_id)
|
||||
|
||||
request = build_request(:delete_customer_payment_profile, options)
|
||||
commit(:delete_customer_payment_profile, request)
|
||||
end
|
||||
|
||||
# Deletes a customer shipping address from an existing customer profile.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be deleted. (REQUIRED)
|
||||
# * <tt>:customer_address_id</tt> -- The Shipping Address ID of the shipping address to be deleted. (REQUIRED)
|
||||
def delete_customer_shipping_address(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :customer_address_id)
|
||||
|
||||
request = build_request(:delete_customer_shipping_address, options)
|
||||
commit(:delete_customer_shipping_address, request)
|
||||
end
|
||||
|
||||
# Retrieves an existing customer profile along with all the associated customer payment profiles and customer shipping addresses.
|
||||
#
|
||||
# Returns a Response whose params hash contains all the profile information.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to retrieve. (REQUIRED)
|
||||
def get_customer_profile(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
|
||||
request = build_request(:get_customer_profile, options)
|
||||
commit(:get_customer_profile, request)
|
||||
end
|
||||
|
||||
def get_customer_profile_ids(options = {})
|
||||
request = build_request(:get_customer_profile_ids, options)
|
||||
commit(:get_customer_profile_ids, request)
|
||||
end
|
||||
|
||||
# Retrieve a customer payment profile for an existing customer profile.
|
||||
#
|
||||
# Returns a Response whose params hash contains all the payment profile information. Sensitive information such as credit card
|
||||
# numbers will be masked.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be retrieved. (REQUIRED)
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Payment Profile ID of the payment profile to be retrieved. (REQUIRED)
|
||||
def get_customer_payment_profile(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :customer_payment_profile_id)
|
||||
|
||||
request = build_request(:get_customer_payment_profile, options)
|
||||
commit(:get_customer_payment_profile, request)
|
||||
end
|
||||
|
||||
# Retrieve a customer shipping address for an existing customer profile.
|
||||
#
|
||||
# Returns a Response whose params hash contains all the shipping address information.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be retrieved. (REQUIRED)
|
||||
# * <tt>:customer_address_id</tt> -- The Shipping Address ID of the shipping address to be retrieved. (REQUIRED)
|
||||
def get_customer_shipping_address(options)
|
||||
requires!(options, :customer_profile_id)
|
||||
requires!(options, :customer_address_id)
|
||||
|
||||
request = build_request(:get_customer_shipping_address, options)
|
||||
commit(:get_customer_shipping_address, request)
|
||||
end
|
||||
|
||||
# Updates an existing customer profile.
|
||||
#
|
||||
# Warning: if you do not provide a parameter in the <tt>:payment_profile</tt> hash, it is automatically set to nil at
|
||||
# Authorize.Net. You will most likely want to first get the profile hash using get_customer_profile and then only change the
|
||||
# elements you wish to change.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:profile</tt> -- A hash containing the values the Customer Profile should be updated to. (REQUIRED)
|
||||
#
|
||||
# ==== Profile
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer profile to update. (REQUIRED)
|
||||
def update_customer_profile(options)
|
||||
requires!(options, :profile)
|
||||
requires!(options[:profile], :customer_profile_id)
|
||||
|
||||
request = build_request(:update_customer_profile, options)
|
||||
commit(:update_customer_profile, request)
|
||||
end
|
||||
|
||||
# Updates a customer payment profile for an existing customer profile.
|
||||
#
|
||||
# Warning: if you do not provide a parameter in the <tt>:payment_profile</tt> hash, it is automatically set to nil at
|
||||
# Authorize.Net. You will most likely want to first get the profile hash using get_customer_payment_profile and then only
|
||||
# change the elements you wish to change.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be updated. (REQUIRED)
|
||||
# * <tt>:payment_profile</tt> -- A hash containing the values the Customer Payment Profile should be updated to. (REQUIRED)
|
||||
#
|
||||
# ==== Payment Profile
|
||||
#
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to update. (REQUIRED)
|
||||
def update_customer_payment_profile(options)
|
||||
requires!(options, :customer_profile_id, :payment_profile)
|
||||
requires!(options[:payment_profile], :customer_payment_profile_id)
|
||||
|
||||
request = build_request(:update_customer_payment_profile, options)
|
||||
commit(:update_customer_payment_profile, request)
|
||||
end
|
||||
|
||||
# Updates a customer shipping address for an existing customer profile.
|
||||
#
|
||||
# Warning: if you do not provide a parameter in the <tt>:address</tt> hash, it is automatically set to nil at
|
||||
# Authorize.Net. You will most likely want to first get the profile hash using get_customer_shipping_address and then only
|
||||
# change the elements you wish to change.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer with the payment profile to be updated. (REQUIRED)
|
||||
# * <tt>:address</tt> -- A hash containing the values the Customer Shipping Address should be updated to. (REQUIRED)
|
||||
#
|
||||
# ==== Address
|
||||
#
|
||||
# * <tt>:customer_address_id</tt> -- The Customer Address ID of the Customer Payment Profile to update. (REQUIRED)
|
||||
def update_customer_shipping_address(options)
|
||||
requires!(options, :customer_profile_id, :address)
|
||||
requires!(options[:address], :customer_address_id)
|
||||
|
||||
request = build_request(:update_customer_shipping_address, options)
|
||||
commit(:update_customer_shipping_address, request)
|
||||
end
|
||||
|
||||
# Creates a new payment transaction from an existing customer profile
|
||||
#
|
||||
# This is what is used to charge a customer whose information you have stored in a Customer Profile.
|
||||
#
|
||||
# Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:transaction</tt> -- A hash containing information on the transaction that is being requested. (REQUIRED)
|
||||
#
|
||||
# ==== Transaction
|
||||
#
|
||||
# * <tt>:type</tt> -- The type of transaction. Can be either <tt>:auth_only</tt>, <tt>:capture_only</tt>, <tt>:auth_capture</tt>, <tt>:prior_auth_capture</tt>, <tt>:refund</tt> or <tt>:void</tt>. (REQUIRED)
|
||||
# * <tt>:amount</tt> -- The amount for the tranaction. Formatted with a decimal. For example "4.95" (CONDITIONAL)
|
||||
# - :type == :void (NOT USED)
|
||||
# - :type == :refund (OPTIONAL)
|
||||
# - :type == (:auth_only, :capture_only, :auth_capture, :prior_auth_capture) (REQUIRED)
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL)
|
||||
# - :type == (:void, :prior_auth_capture) (OPTIONAL)
|
||||
# - :type == :refund (CONDITIONAL - required if masked information is not being submitted [see below])
|
||||
# - :type == (:auth_only, :capture_only, :auth_capture) (REQUIRED)
|
||||
#
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (CONDITIONAL)
|
||||
# - :type == (:void, :prior_auth_capture) (OPTIONAL)
|
||||
# - :type == :refund (CONDITIONAL - required if masked information is not being submitted [see below])
|
||||
# - :type == (:auth_only, :capture_only, :auth_capture) (REQUIRED)
|
||||
#
|
||||
# * <tt>:trans_id</tt> -- The payment gateway assigned transaction ID of the original transaction (CONDITIONAL):
|
||||
# - :type = (:void, :refund, :prior_auth_capture) (REQUIRED)
|
||||
# - :type = (:auth_only, :capture_only, :auth_capture) (NOT USED)
|
||||
#
|
||||
# * <tt>:card_code</tt> -- CVV/CCV code (OPTIONAL)
|
||||
# - :type = (:void, :refund, :prior_auth_capture) (NOT USED)
|
||||
# - :type = (:auth_only, :capture_only, :auth_capture) (OPTIONAL)
|
||||
#
|
||||
# * <tt>:customer_shipping_address_id</tt> -- Payment gateway assigned ID associated with the customer shipping address (CONDITIONAL)
|
||||
# - :type = (:void, :refund) (OPTIONAL)
|
||||
# - :type = (:auth_only, :capture_only, :auth_capture) (NOT USED)
|
||||
# - :type = (:prior_auth_capture) (OPTIONAL)
|
||||
#
|
||||
# ==== For :type == :refund only
|
||||
# * <tt>:credit_card_number_masked</tt> -- (CONDITIONAL - requied for credit card refunds is :customer_profile_id AND :customer_payment_profile_id are missing)
|
||||
# * <tt>:bank_routing_number_masked && :bank_account_number_masked</tt> -- (CONDITIONAL - requied for electronic check refunds is :customer_profile_id AND :customer_payment_profile_id are missing) (NOT ABLE TO TEST - I keep getting "ACH transactions are not accepted by this merchant." when trying to make a payment and, until that's possible I can't refund (wiseleyb@gmail.com))
|
||||
def create_customer_profile_transaction(options)
|
||||
requires!(options, :transaction)
|
||||
requires!(options[:transaction], :type)
|
||||
case options[:transaction][:type]
|
||||
when :void
|
||||
requires!(options[:transaction], :trans_id)
|
||||
when :refund
|
||||
requires!(options[:transaction], :trans_id) &&
|
||||
(
|
||||
(options[:transaction][:customer_profile_id] && options[:transaction][:customer_payment_profile_id]) ||
|
||||
options[:transaction][:credit_card_number_masked] ||
|
||||
(options[:transaction][:bank_routing_number_masked] && options[:transaction][:bank_account_number_masked])
|
||||
)
|
||||
when :prior_auth_capture
|
||||
requires!(options[:transaction], :amount, :trans_id)
|
||||
else
|
||||
requires!(options[:transaction], :amount, :customer_profile_id, :customer_payment_profile_id)
|
||||
end
|
||||
request = build_request(:create_customer_profile_transaction, options)
|
||||
commit(:create_customer_profile_transaction, request)
|
||||
end
|
||||
|
||||
# Creates a new payment transaction for refund from an existing customer profile
|
||||
#
|
||||
# This is what is used to refund a transaction you have stored in a Customer Profile.
|
||||
#
|
||||
# Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:transaction</tt> -- A hash containing information on the transaction that is being requested. (REQUIRED)
|
||||
#
|
||||
# ==== Transaction
|
||||
#
|
||||
# * <tt>:amount</tt> -- The total amount to be refunded (REQUIRED)
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL :customer_payment_profile_id must be included if used)
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (CONDITIONAL :customer_profile_id must be included if used)
|
||||
#
|
||||
# * <tt>:credit_card_number_masked</tt> -- Four Xs follwed by the last four digits of the credit card (CONDITIONAL - used if customer_profile_id and customer_payment_profile_id aren't given)
|
||||
#
|
||||
# * <tt>:bank_routing_number_masked</tt> -- The last four gidits of the routing number to be refunded (CONDITIONAL - must be used with :bank_account_number_masked)
|
||||
# * <tt>:bank_account_number_masked</tt> -- The last four digis of the bank account number to be refunded, Ex. XXXX1234 (CONDITIONAL - must be used with :bank_routing_number_masked)
|
||||
#
|
||||
# * <tt>:tax</tt> - A hash containing tax information for the refund (OPTIONAL - <tt>:amount</tt>, <tt>:name</tt> (31 characters), <tt>:description</tt> (255 characters))
|
||||
# * <tt>:duty</tt> - A hash containting duty information for the refund (OPTIONAL - <tt>:amount</tt>, <tt>:name</tt> (31 characters), <tt>:description</tt> (255 characters))
|
||||
# * <tt>:shipping</tt> - A hash containing shipping information for the refund (OPTIONAL - <tt>:amount</tt>, <tt>:name</tt> (31 characters), <tt>:description</tt> (255 characters))
|
||||
def create_customer_profile_transaction_for_refund(options)
|
||||
requires!(options, :transaction)
|
||||
options[:transaction][:type] = :refund
|
||||
requires!(options[:transaction], :trans_id)
|
||||
requires!(options[:transaction], :amount)
|
||||
request = build_request(:create_customer_profile_transaction, options)
|
||||
commit(:create_customer_profile_transaction, request)
|
||||
end
|
||||
|
||||
# Creates a new payment transaction for void from an existing customer profile
|
||||
#
|
||||
# This is what is used to void a transaction you have stored in a Customer Profile.
|
||||
#
|
||||
# Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:transaction</tt> -- A hash containing information on the transaction that is being requested. (REQUIRED)
|
||||
#
|
||||
# ==== Transaction
|
||||
#
|
||||
# * <tt>:trans_id</tt> -- The payment gateway assigned transaction id of the original transaction. (REQUIRED)
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction.
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction.
|
||||
# * <tt>:customer_shipping_address_id</tt> -- Payment gateway assigned ID associated with the customer shipping address.
|
||||
def create_customer_profile_transaction_for_void(options)
|
||||
requires!(options, :transaction)
|
||||
options[:transaction][:type] = :void
|
||||
requires!(options[:transaction], :trans_id)
|
||||
request = build_request(:create_customer_profile_transaction, options)
|
||||
commit(:create_customer_profile_transaction, request)
|
||||
end
|
||||
|
||||
# Verifies an existing customer payment profile by generating a test transaction
|
||||
#
|
||||
# Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (REQUIRED)
|
||||
# * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to be verified. (REQUIRED)
|
||||
# * <tt>:customer_address_id</tt> -- The Customer Address ID of the Customer Shipping Address to be verified. (OPTIONAL)
|
||||
# * <tt>:card_code</tt> -- If the payment profile is a credit card, the CCV/CVV code to validate with (OPTIONAL)
|
||||
# * <tt>:validation_mode</tt> -- <tt>:live</tt> or <tt>:test</tt> In Test Mode, only field validation is performed. (REQUIRED
|
||||
# In Live Mode, a transaction is generated and submitted to the processor with the amount of $0.01. If successful, the transaction is immediately voided. (REQUIRED)
|
||||
def validate_customer_payment_profile(options)
|
||||
requires!(options, :customer_profile_id, :customer_payment_profile_id, :validation_mode)
|
||||
|
||||
request = build_request(:validate_customer_payment_profile, options)
|
||||
commit(:validate_customer_payment_profile, request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expdate(credit_card)
|
||||
if credit_card.year.present? && credit_card.month.present?
|
||||
sprintf('%04d-%02d', credit_card.year, credit_card.month)
|
||||
else
|
||||
'XXXX'
|
||||
end
|
||||
end
|
||||
|
||||
def build_request(action, options = {})
|
||||
unless CIM_ACTIONS.include?(action)
|
||||
raise StandardError, "Invalid Customer Information Manager Action: #{action}"
|
||||
end
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
||||
xml.tag!("#{CIM_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_CIM_NAMESPACE) do
|
||||
add_merchant_authentication(xml)
|
||||
# Merchant-assigned reference ID for the request
|
||||
xml.tag!('refId', options[:ref_id]) if options[:ref_id]
|
||||
# Order options
|
||||
add_order(xml, options[:order]) if options[:order]
|
||||
send("build_#{action}_request", xml, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Contains the merchant’s payment gateway account authentication information
|
||||
def add_merchant_authentication(xml)
|
||||
xml.tag!('merchantAuthentication') do
|
||||
xml.tag!('name', @options[:login])
|
||||
xml.tag!('transactionKey', @options[:password])
|
||||
end
|
||||
end
|
||||
|
||||
def build_create_customer_profile_request(xml, options)
|
||||
add_profile(xml, options[:profile])
|
||||
|
||||
xml.tag!('validationMode', CIM_VALIDATION_MODES[options[:validation_mode]]) if options[:validation_mode]
|
||||
|
||||
if options.has_key?(:payment_profile)
|
||||
xml.tag!('paymentProfile') do
|
||||
add_payment_profile(xml, options[:payment_profile])
|
||||
end
|
||||
end
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_create_customer_payment_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
|
||||
xml.tag!('paymentProfile') do
|
||||
add_payment_profile(xml, options[:payment_profile])
|
||||
end
|
||||
|
||||
xml.tag!('validationMode', CIM_VALIDATION_MODES[options[:validation_mode]]) if options[:validation_mode]
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_create_customer_shipping_address_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
|
||||
xml.tag!('address') do
|
||||
add_address(xml, options[:address])
|
||||
end
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_delete_customer_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_delete_customer_payment_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.tag!('customerPaymentProfileId', options[:customer_payment_profile_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_delete_customer_shipping_address_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.tag!('customerAddressId', options[:customer_address_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_get_customer_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_get_customer_profile_ids_request(xml, options)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_get_customer_payment_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.tag!('customerPaymentProfileId', options[:customer_payment_profile_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_get_customer_shipping_address_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.tag!('customerAddressId', options[:customer_address_id])
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_update_customer_profile_request(xml, options)
|
||||
add_profile(xml, options[:profile], true)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_update_customer_payment_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
|
||||
xml.tag!('paymentProfile') do
|
||||
add_payment_profile(xml, options[:payment_profile])
|
||||
end
|
||||
|
||||
xml.tag!('validationMode', CIM_VALIDATION_MODES[options[:validation_mode]]) if options[:validation_mode]
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_update_customer_shipping_address_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
|
||||
xml.tag!('address') do
|
||||
add_address(xml, options[:address])
|
||||
end
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_create_customer_profile_transaction_request(xml, options)
|
||||
options[:extra_options] ||= {}
|
||||
options[:extra_options].merge!('x_test_request' => 'TRUE') if @options[:test]
|
||||
|
||||
add_transaction(xml, options[:transaction])
|
||||
tag_unless_blank(xml, 'extraOptions', format_extra_options(options[:extra_options]))
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_validate_customer_payment_profile_request(xml, options)
|
||||
xml.tag!('customerProfileId', options[:customer_profile_id])
|
||||
xml.tag!('customerPaymentProfileId', options[:customer_payment_profile_id])
|
||||
xml.tag!('customerShippingAddressId', options[:customer_address_id]) if options[:customer_address_id]
|
||||
tag_unless_blank(xml, 'cardCode', options[:card_code])
|
||||
xml.tag!('validationMode', CIM_VALIDATION_MODES[options[:validation_mode]]) if options[:validation_mode]
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# :merchant_customer_id (Optional)
|
||||
# :description (Optional)
|
||||
# :email (Optional)
|
||||
# :payment_profiles (Optional)
|
||||
def add_profile(xml, profile, update = false)
|
||||
xml.tag!('profile') do
|
||||
# Merchant assigned ID for the customer. Up to 20 characters. (optional)
|
||||
xml.tag!('merchantCustomerId', profile[:merchant_customer_id]) if profile[:merchant_customer_id]
|
||||
# Description of the customer. Up to 255 Characters (optional)
|
||||
xml.tag!('description', profile[:description]) if profile[:description]
|
||||
# Email Address for the customer. Up to 255 Characters (optional)
|
||||
xml.tag!('email', profile[:email]) if profile[:email]
|
||||
|
||||
if update
|
||||
xml.tag!('customerProfileId', profile[:customer_profile_id])
|
||||
else
|
||||
add_payment_profiles(xml, profile[:payment_profiles]) if profile[:payment_profiles]
|
||||
add_ship_to_list(xml, profile[:ship_to_list]) if profile[:ship_to_list]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_transaction(xml, transaction)
|
||||
unless CIM_TRANSACTION_TYPES.include?(transaction[:type])
|
||||
raise StandardError, "Invalid Customer Information Manager Transaction Type: #{transaction[:type]}"
|
||||
end
|
||||
|
||||
xml.tag!('transaction') do
|
||||
xml.tag!(CIM_TRANSACTION_TYPES[transaction[:type]]) do
|
||||
# The amount to be billed to the customer
|
||||
case transaction[:type]
|
||||
when :void
|
||||
tag_unless_blank(xml,'customerProfileId', transaction[:customer_profile_id])
|
||||
tag_unless_blank(xml,'customerPaymentProfileId', transaction[:customer_payment_profile_id])
|
||||
tag_unless_blank(xml,'customerShippingAddressId', transaction[:customer_shipping_address_id])
|
||||
xml.tag!('transId', transaction[:trans_id])
|
||||
when :refund
|
||||
#TODO - add lineItems field
|
||||
xml.tag!('amount', transaction[:amount])
|
||||
tag_unless_blank(xml, 'customerProfileId', transaction[:customer_profile_id])
|
||||
tag_unless_blank(xml, 'customerPaymentProfileId', transaction[:customer_payment_profile_id])
|
||||
tag_unless_blank(xml, 'customerShippingAddressId', transaction[:customer_shipping_address_id])
|
||||
tag_unless_blank(xml, 'creditCardNumberMasked', transaction[:credit_card_number_masked])
|
||||
tag_unless_blank(xml, 'bankRoutingNumberMasked', transaction[:bank_routing_number_masked])
|
||||
tag_unless_blank(xml, 'bankAccountNumberMasked', transaction[:bank_account_number_masked])
|
||||
xml.tag!('transId', transaction[:trans_id])
|
||||
add_tax(xml, transaction[:tax]) if transaction[:tax]
|
||||
add_duty(xml, transaction[:duty]) if transaction[:duty]
|
||||
add_shipping(xml, transaction[:shipping]) if transaction[:shipping]
|
||||
when :prior_auth_capture
|
||||
xml.tag!('amount', transaction[:amount])
|
||||
xml.tag!('transId', transaction[:trans_id])
|
||||
else
|
||||
xml.tag!('amount', transaction[:amount])
|
||||
xml.tag!('customerProfileId', transaction[:customer_profile_id])
|
||||
xml.tag!('customerPaymentProfileId', transaction[:customer_payment_profile_id])
|
||||
xml.tag!('approvalCode', transaction[:approval_code]) if transaction[:type] == :capture_only
|
||||
end
|
||||
add_order(xml, transaction[:order]) if transaction[:order].present?
|
||||
unless [:void,:refund,:prior_auth_capture].include?(transaction[:type])
|
||||
tag_unless_blank(xml, 'cardCode', transaction[:card_code])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_tax(xml, tax)
|
||||
xml.tag!('tax') do
|
||||
xml.tag!('amount', tax[:amount]) if tax[:amount]
|
||||
xml.tag!('name', tax[:name]) if tax[:name]
|
||||
xml.tag!('description', tax[:description]) if tax[:description]
|
||||
end
|
||||
end
|
||||
|
||||
def add_duty(xml, duty)
|
||||
xml.tag!('duty') do
|
||||
xml.tag!('amount', duty[:amount]) if duty[:amount]
|
||||
xml.tag!('name', duty[:name]) if duty[:name]
|
||||
xml.tag!('description', duty[:description]) if duty[:description]
|
||||
end
|
||||
end
|
||||
|
||||
def add_shipping(xml, shipping)
|
||||
xml.tag!('shipping') do
|
||||
xml.tag!('amount', shipping[:amount]) if shipping[:amount]
|
||||
xml.tag!('name', shipping[:name]) if shipping[:name]
|
||||
xml.tag!('description', shipping[:description]) if shipping[:description]
|
||||
end
|
||||
end
|
||||
|
||||
def add_order(xml, order)
|
||||
xml.tag!('order') do
|
||||
xml.tag!('invoiceNumber', order[:invoice_number]) if order[:invoice_number]
|
||||
xml.tag!('description', order[:description]) if order[:description]
|
||||
xml.tag!('purchaseOrderNumber', order[:purchase_order_number]) if order[:purchase_order_number]
|
||||
end
|
||||
end
|
||||
|
||||
def add_payment_profiles(xml, payment_profiles)
|
||||
xml.tag!('paymentProfiles') do
|
||||
add_payment_profile(xml, payment_profiles)
|
||||
end
|
||||
end
|
||||
|
||||
# :customer_type => 'individual or business', # Optional
|
||||
# :bill_to => @address,
|
||||
# :payment => @payment
|
||||
def add_payment_profile(xml, payment_profile)
|
||||
# 'individual' or 'business' (optional)
|
||||
xml.tag!('customerType', payment_profile[:customer_type]) if payment_profile[:customer_type]
|
||||
|
||||
if payment_profile[:bill_to]
|
||||
xml.tag!('billTo') do
|
||||
add_address(xml, payment_profile[:bill_to])
|
||||
end
|
||||
end
|
||||
|
||||
if payment_profile[:payment]
|
||||
xml.tag!('payment') do
|
||||
add_credit_card(xml, payment_profile[:payment][:credit_card]) if payment_profile[:payment].has_key?(:credit_card)
|
||||
add_bank_account(xml, payment_profile[:payment][:bank_account]) if payment_profile[:payment].has_key?(:bank_account)
|
||||
add_drivers_license(xml, payment_profile[:payment][:drivers_license]) if payment_profile[:payment].has_key?(:drivers_license)
|
||||
# This element is only required for Wells Fargo SecureSource eCheck.Net merchants
|
||||
# The customer's Social Security Number or Tax ID
|
||||
xml.tag!('taxId', payment_profile[:payment]) if payment_profile[:payment].has_key?(:tax_id)
|
||||
end
|
||||
end
|
||||
|
||||
xml.tag!('customerPaymentProfileId', payment_profile[:customer_payment_profile_id]) if payment_profile[:customer_payment_profile_id]
|
||||
end
|
||||
|
||||
def add_ship_to_list(xml, ship_to_list)
|
||||
xml.tag!('shipToList') do
|
||||
add_address(xml, ship_to_list)
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(xml, address)
|
||||
xml.tag!('firstName', address[:first_name])
|
||||
xml.tag!('lastName', address[:last_name])
|
||||
xml.tag!('company', address[:company])
|
||||
xml.tag!('address', address[:address1]) if address[:address1]
|
||||
xml.tag!('address', address[:address]) if address[:address]
|
||||
xml.tag!('city', address[:city])
|
||||
xml.tag!('state', address[:state])
|
||||
xml.tag!('zip', address[:zip])
|
||||
xml.tag!('country', address[:country])
|
||||
xml.tag!('phoneNumber', address[:phone_number]) if address[:phone_number]
|
||||
xml.tag!('faxNumber', address[:fax_number]) if address[:fax_number]
|
||||
|
||||
xml.tag!('customerAddressId', address[:customer_address_id]) if address[:customer_address_id]
|
||||
end
|
||||
|
||||
# Adds customer’s credit card information
|
||||
# Note: This element should only be included
|
||||
# when the payment method is credit card.
|
||||
def add_credit_card(xml, credit_card)
|
||||
return unless credit_card
|
||||
xml.tag!('creditCard') do
|
||||
# The credit card number used for payment of the subscription
|
||||
xml.tag!('cardNumber', credit_card.number)
|
||||
# The expiration date of the credit card used for the subscription
|
||||
xml.tag!('expirationDate', expdate(credit_card))
|
||||
# Note that Authorize.net does not save CVV codes as part of the
|
||||
# payment profile. Any transactions/validations after the payment
|
||||
# profile is created that wish to use CVV verification must pass
|
||||
# the CVV code to authorize.net again.
|
||||
xml.tag!('cardCode', credit_card.verification_value) if credit_card.verification_value?
|
||||
end
|
||||
end
|
||||
|
||||
# Adds customer’s bank account information
|
||||
# Note: This element should only be included
|
||||
# when the payment method is bank account.
|
||||
def add_bank_account(xml, bank_account)
|
||||
raise StandardError, "Invalid Bank Account Type: #{bank_account[:account_type]}" unless BANK_ACCOUNT_TYPES.include?(bank_account[:account_type])
|
||||
raise StandardError, "Invalid eCheck Type: #{bank_account[:echeck_type]}" unless ECHECK_TYPES.include?(bank_account[:echeck_type])
|
||||
|
||||
xml.tag!('bankAccount') do
|
||||
# The type of bank account
|
||||
xml.tag!('accountType', BANK_ACCOUNT_TYPES[bank_account[:account_type]])
|
||||
# The routing number of the customer’s bank
|
||||
xml.tag!('routingNumber', bank_account[:routing_number])
|
||||
# The bank account number
|
||||
xml.tag!('accountNumber', bank_account[:account_number])
|
||||
# The full name of the individual associated
|
||||
# with the bank account number
|
||||
xml.tag!('nameOnAccount', bank_account[:name_on_account])
|
||||
# The type of electronic check transaction
|
||||
xml.tag!('echeckType', ECHECK_TYPES[bank_account[:echeck_type]])
|
||||
# The full name of the individual associated
|
||||
# with the bank account number (optional)
|
||||
xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
|
||||
end
|
||||
end
|
||||
|
||||
# Adds customer’s driver's license information
|
||||
# Note: This element is only required for
|
||||
# Wells Fargo SecureSource eCheck.Net merchants
|
||||
def add_drivers_license(xml, drivers_license)
|
||||
xml.tag!('driversLicense') do
|
||||
# The state of the customer's driver's license
|
||||
# A valid two character state code
|
||||
xml.tag!('state', drivers_license[:state])
|
||||
# The customer’s driver's license number
|
||||
xml.tag!('number', drivers_license[:number])
|
||||
# The date of birth listed on the customer's driver's license
|
||||
# YYYY-MM-DD
|
||||
xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
|
||||
end
|
||||
end
|
||||
|
||||
def commit(action, request)
|
||||
url = test? ? test_url : live_url
|
||||
xml = ssl_post(url, request, "Content-Type" => "text/xml")
|
||||
|
||||
response_params = parse(action, xml)
|
||||
|
||||
message = response_params['messages']['message']['text']
|
||||
test_mode = test? || message =~ /Test Mode/
|
||||
success = response_params['messages']['result_code'] == 'Ok'
|
||||
response_params['direct_response'] = parse_direct_response(response_params['direct_response']) if response_params['direct_response']
|
||||
transaction_id = response_params['direct_response']['transaction_id'] if response_params['direct_response']
|
||||
|
||||
Response.new(success, message, response_params,
|
||||
:test => test_mode,
|
||||
:authorization => transaction_id || response_params['customer_profile_id'] || (response_params['profile'] ? response_params['profile']['customer_profile_id'] : nil)
|
||||
)
|
||||
end
|
||||
|
||||
def tag_unless_blank(xml, tag_name, data)
|
||||
xml.tag!(tag_name, data) unless data.blank? || data.nil?
|
||||
end
|
||||
|
||||
def format_extra_options(options)
|
||||
options.map{ |k, v| "#{k}=#{v}" }.join(',') unless options.nil?
|
||||
end
|
||||
|
||||
def parse_direct_response(params)
|
||||
delimiter = @options[:delimiter] || ','
|
||||
direct_response = {'raw' => params}
|
||||
direct_response_fields = params.split(delimiter)
|
||||
direct_response.merge(
|
||||
{
|
||||
'response_code' => direct_response_fields[0],
|
||||
'response_subcode' => direct_response_fields[1],
|
||||
'response_reason_code' => direct_response_fields[2],
|
||||
'message' => direct_response_fields[3],
|
||||
'approval_code' => direct_response_fields[4],
|
||||
'avs_response' => direct_response_fields[5],
|
||||
'transaction_id' => direct_response_fields[6],
|
||||
'invoice_number' => direct_response_fields[7],
|
||||
'order_description' => direct_response_fields[8],
|
||||
'amount' => direct_response_fields[9],
|
||||
'method' => direct_response_fields[10],
|
||||
'transaction_type' => direct_response_fields[11],
|
||||
'customer_id' => direct_response_fields[12],
|
||||
'first_name' => direct_response_fields[13],
|
||||
'last_name' => direct_response_fields[14],
|
||||
'company' => direct_response_fields[15],
|
||||
'address' => direct_response_fields[16],
|
||||
'city' => direct_response_fields[17],
|
||||
'state' => direct_response_fields[18],
|
||||
'zip_code' => direct_response_fields[19],
|
||||
'country' => direct_response_fields[20],
|
||||
'phone' => direct_response_fields[21],
|
||||
'fax' => direct_response_fields[22],
|
||||
'email_address' => direct_response_fields[23],
|
||||
'ship_to_first_name' => direct_response_fields[24],
|
||||
'ship_to_last_name' => direct_response_fields[25],
|
||||
'ship_to_company' => direct_response_fields[26],
|
||||
'ship_to_address' => direct_response_fields[27],
|
||||
'ship_to_city' => direct_response_fields[28],
|
||||
'ship_to_state' => direct_response_fields[29],
|
||||
'ship_to_zip_code' => direct_response_fields[30],
|
||||
'ship_to_country' => direct_response_fields[31],
|
||||
'tax' => direct_response_fields[32],
|
||||
'duty' => direct_response_fields[33],
|
||||
'freight' => direct_response_fields[34],
|
||||
'tax_exempt' => direct_response_fields[35],
|
||||
'purchase_order_number' => direct_response_fields[36],
|
||||
'md5_hash' => direct_response_fields[37],
|
||||
'card_code' => direct_response_fields[38],
|
||||
'cardholder_authentication_verification_response' => direct_response_fields[39],
|
||||
# The following direct response fields are only available in version 3.1 of the
|
||||
# transaction response. Check your merchant account settings for details.
|
||||
'account_number' => direct_response_fields[50] || '',
|
||||
'card_type' => direct_response_fields[51] || '',
|
||||
'split_tender_id' => direct_response_fields[52] || '',
|
||||
'requested_amount' => direct_response_fields[53] || '',
|
||||
'balance_on_card' => direct_response_fields[54] || '',
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def parse(action, xml)
|
||||
xml = REXML::Document.new(xml)
|
||||
root = REXML::XPath.first(xml, "//#{CIM_ACTIONS[action]}Response") ||
|
||||
REXML::XPath.first(xml, "//ErrorResponse")
|
||||
if root
|
||||
response = parse_element(root)
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def parse_element(node)
|
||||
if node.has_elements?
|
||||
response = {}
|
||||
node.elements.each{ |e|
|
||||
key = e.name.underscore
|
||||
value = parse_element(e)
|
||||
if response.has_key?(key)
|
||||
if response[key].is_a?(Array)
|
||||
response[key].push(value)
|
||||
else
|
||||
response[key] = [response[key], value]
|
||||
end
|
||||
else
|
||||
response[key] = parse_element(e)
|
||||
end
|
||||
}
|
||||
else
|
||||
response = node.text
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
467
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/balanced.rb
vendored
Normal file
467
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/balanced.rb
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
require 'json'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
|
||||
# For more information on Balanced visit https://www.balancedpayments.com
|
||||
# or visit #balanced on irc.freenode.net
|
||||
#
|
||||
# Instantiate a instance of BalancedGateway by passing through your
|
||||
# Balanced API key secret.
|
||||
#
|
||||
# ==== To obtain an API key of your own
|
||||
#
|
||||
# 1. Visit https://www.balancedpayments.com
|
||||
# 2. Click "Get started"
|
||||
# 3. The next screen will give you a test API key of your own
|
||||
# 4. When you're ready to generate a production API key click the "Go
|
||||
# live" button on the Balanced dashboard and fill in your marketplace
|
||||
# details.
|
||||
#
|
||||
# ==== Overview
|
||||
#
|
||||
# Balanced provides a RESTful API, all entities within Balanced are
|
||||
# represented by their respective URIs, these are returned in the
|
||||
# `authorization` parameter of the Active Merchant Response object.
|
||||
#
|
||||
# All Response objects will contain a hash property called `params` which
|
||||
# holds the raw JSON dictionary returned by Balanced. You can find
|
||||
# properties about the operation performed and the object that represents
|
||||
# it within this hash.
|
||||
#
|
||||
# All operations within Balanced are tied to an account, as such, when you
|
||||
# perform an `authorization` or a `capture` with a new credit card you
|
||||
# must ensure you also pass the `:email` property within the `options`
|
||||
# parameter.
|
||||
#
|
||||
# For more details about Balanced's API visit:
|
||||
# https://www.balancedpayments.com/docs
|
||||
#
|
||||
# ==== Terminology & Transaction Flow
|
||||
#
|
||||
# * An `authorization` operation will return a Hold URI. An `authorization`
|
||||
# within Balanced is valid until the `expires_at` property. You can see the
|
||||
# exact date of the expiry on the Response object by inspecting the
|
||||
# property `response.params['expires_at']`. The resulting Hold may be
|
||||
# `capture`d or `void`ed at any time before the `expires_at` date for
|
||||
# any amount up to the full amount of the original `authorization`.
|
||||
# * A `capture` operation will return a Debit URI. You must pass the URI of
|
||||
# the previously performed `authorization`
|
||||
# * A `purchase` will create a Hold and Debit in a single operation and
|
||||
# return the URI of the resulting Debit.
|
||||
# * A `void` operation must be performed on an existing `authorization`
|
||||
# and will result in releasing the funds reserved by the
|
||||
# `authorization`.
|
||||
# * The `refund` operation must be performed on a previously captured
|
||||
# Debit URI. You may refund any fraction of the original amount of the
|
||||
# debit up to the original total.
|
||||
#
|
||||
class BalancedGateway < Gateway
|
||||
VERSION = '1.0.0'
|
||||
|
||||
TEST_URL = LIVE_URL = 'https://api.balancedpayments.com'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO
|
||||
# country codes
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.homepage_url = 'https://www.balancedpayments.com/'
|
||||
self.display_name = 'Balanced'
|
||||
self.money_format = :cents
|
||||
|
||||
class Error < StandardError
|
||||
attr_reader :response
|
||||
|
||||
def initialize(response, msg=nil)
|
||||
@response = response
|
||||
super(msg || response['description'])
|
||||
end
|
||||
end
|
||||
|
||||
class CardDeclined < Error
|
||||
end
|
||||
|
||||
# Creates a new BalancedGateway
|
||||
#
|
||||
# The gateway requires that a valid api_key be passed in the +options+
|
||||
# hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The Balanced API Secret (REQUIRED)
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
initialize_marketplace(options[:marketplace] || load_marketplace)
|
||||
end
|
||||
|
||||
# Performs an authorization (Hold in Balanced nonclementure), which
|
||||
# reserves the funds on the customer's credit card, but does not charge
|
||||
# the card. An authorization is valid until the `expires_at` field in
|
||||
# the params Hash passes. See `response.params['expires_at']`. The exact
|
||||
# amount of time until an authorization expires depends on the card
|
||||
# issuer.
|
||||
#
|
||||
# If you pass a previously tokenized `credit_card` URI the only other
|
||||
# parameter required is `money`. If you pass `credit_card` as a hash of
|
||||
# credit card information you must also pass `options` with a `:email`
|
||||
# entry.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> -- A hash of credit card details for this
|
||||
# transaction or the URI of a card previously stored in Balanced.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# If you are passing a new credit card you must pass one of these two
|
||||
# parameters
|
||||
#
|
||||
# * <tt>email</tt> -- the email address of user associated with this
|
||||
# purchase.
|
||||
# * <tt>account_uri</tt> -- `account_uri` is the URI of an existing
|
||||
# Balanced account.
|
||||
def authorize(money, credit_card, options = {})
|
||||
if credit_card.respond_to?(:number)
|
||||
requires!(options, :email) unless options[:account_uri]
|
||||
end
|
||||
|
||||
post = {}
|
||||
post[:amount] = money
|
||||
post[:description] = options[:description]
|
||||
|
||||
create_or_find_account(post, options)
|
||||
add_credit_card(post, credit_card, options)
|
||||
add_address(credit_card, options)
|
||||
|
||||
create_transaction(:post, @holds_uri, post)
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
# Perform a purchase, which is an authorization and capture in a single
|
||||
# operation.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> -- A hash of credit card details for this
|
||||
# transaction or the URI of a card previously stored in Balanced.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# If you are passing a new credit card you must pass one of these two
|
||||
# parameters
|
||||
#
|
||||
# * <tt>email</tt> -- the email address of user associated with this
|
||||
# purchase.
|
||||
# * <tt>account_uri</tt> -- `account_uri` is the URI of an existing
|
||||
# Balanced account.
|
||||
def purchase(money, credit_card, options = {})
|
||||
if credit_card.respond_to?('number')
|
||||
requires!(options, :email) unless options[:account_uri]
|
||||
end
|
||||
|
||||
post = {}
|
||||
post[:amount] = money
|
||||
post[:description] = options[:description]
|
||||
|
||||
create_or_find_account(post, options)
|
||||
add_credit_card(post, credit_card, options)
|
||||
add_address(credit_card, options)
|
||||
|
||||
create_transaction(:post, @debits_uri, post)
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
# Captures the funds from an authorized transaction (Hold).
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as an Integer value in
|
||||
# cents. If omitted the full amount of the original authorization
|
||||
# transaction will be captured.
|
||||
# * <tt>authorization</tt> -- The uri of an authorization returned from
|
||||
# an authorize request.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>description</tt> -- A string that will be displayed on the
|
||||
# Balanced dashboard
|
||||
def capture(money, authorization, options = {})
|
||||
post = {}
|
||||
post[:hold_uri] = authorization
|
||||
post[:amount] = money if money
|
||||
post[:description] = options[:description] if options[:description]
|
||||
post[:on_behalf_of_uri] = options[:on_behalf_of_uri] if options[:on_behalf_of_uri]
|
||||
|
||||
create_transaction(:post, @debits_uri, post)
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
# Void a previous authorization (Hold)
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>authorization</tt> -- The uri of the authorization returned from
|
||||
# an `authorize` request.
|
||||
def void(authorization)
|
||||
post = {}
|
||||
post[:is_void] = true
|
||||
|
||||
create_transaction(:put, authorization, post)
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
# Refund a transaction.
|
||||
#
|
||||
# Returns the money debited from a card to the card from the
|
||||
# marketplace's escrow balance.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>debit_uri</tt> -- The uri of the original transaction against
|
||||
# which the refund is being issued.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>`:amount`<tt> -- specify an amount if you want to perform a
|
||||
# partial refund. This value will default to the total amount of the
|
||||
# debit that has not been refunded so far.
|
||||
def refund(amount, debit_uri = "deprecated", options = {})
|
||||
if(debit_uri == "deprecated" || debit_uri.kind_of?(Hash))
|
||||
deprecated "Calling the refund method without an amount parameter is deprecated and will be removed in a future version."
|
||||
return refund(options[:amount], amount, options)
|
||||
end
|
||||
|
||||
requires!(debit_uri)
|
||||
post = {}
|
||||
post[:debit_uri] = debit_uri
|
||||
post[:amount] = amount
|
||||
post[:description] = options[:description]
|
||||
create_transaction(:post, @refunds_uri, post)
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
# Stores a card and email address
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>credit_card</tt> --
|
||||
def store(credit_card, options = {})
|
||||
requires!(options, :email)
|
||||
post = {}
|
||||
account_uri = create_or_find_account(post, options)
|
||||
if credit_card.respond_to? :number
|
||||
add_credit_card(post, credit_card, options)
|
||||
else
|
||||
associate_card_to_account(account_uri, credit_card)
|
||||
credit_card
|
||||
end
|
||||
rescue Error => ex
|
||||
failed_response(ex.response)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Load URIs for this marketplace by inspecting the marketplace object
|
||||
# returned from the uri. http://en.wikipedia.org/wiki/HATEOAS
|
||||
def load_marketplace
|
||||
response = http_request(:get, '/v1/marketplaces')
|
||||
if error?(response)
|
||||
raise Error.new(response, 'Invalid login credentials supplied')
|
||||
end
|
||||
response['items'][0]
|
||||
end
|
||||
|
||||
def initialize_marketplace(marketplace)
|
||||
@marketplace_uri = marketplace['uri']
|
||||
@holds_uri = marketplace['holds_uri']
|
||||
@debits_uri = marketplace['debits_uri']
|
||||
@cards_uri = marketplace['cards_uri']
|
||||
@accounts_uri = marketplace['accounts_uri']
|
||||
@refunds_uri = marketplace['refunds_uri']
|
||||
end
|
||||
|
||||
def create_or_find_account(post, options)
|
||||
account_uri = nil
|
||||
|
||||
if options.has_key? :account_uri
|
||||
account_uri = options[:account_uri]
|
||||
end
|
||||
|
||||
if account_uri == nil
|
||||
post[:email_address] = options[:email]
|
||||
|
||||
# create an account
|
||||
response = http_request(:post, @accounts_uri, post)
|
||||
|
||||
if response.has_key? 'uri'
|
||||
account_uri = response['uri']
|
||||
elsif error?(response)
|
||||
# lookup account from Balanced, account_uri should be in the
|
||||
# exception in a dictionary called extras
|
||||
account_uri = response['extras']['account_uri']
|
||||
end
|
||||
end
|
||||
|
||||
post[:account_uri] = account_uri
|
||||
|
||||
account_uri
|
||||
end
|
||||
|
||||
def add_address(credit_card, options)
|
||||
return unless credit_card.kind_of?(Hash)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
credit_card[:street_address] = address[:address1] if address[:address1]
|
||||
credit_card[:street_address] += ' ' + address[:address2] if address[:address2]
|
||||
credit_card[:postal_code] = address[:zip] if address[:zip]
|
||||
credit_card[:country] = address[:country] if address[:country]
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_card(post, credit_card, options)
|
||||
if credit_card.respond_to? :number
|
||||
card = {}
|
||||
card[:card_number] = credit_card.number
|
||||
card[:expiration_month] = credit_card.month
|
||||
card[:expiration_year] = credit_card.year
|
||||
card[:security_code] = credit_card.verification_value if credit_card.verification_value?
|
||||
card[:name] = credit_card.name if credit_card.name
|
||||
|
||||
add_address(card, options)
|
||||
|
||||
response = http_request(:post, @cards_uri, card)
|
||||
if error?(response)
|
||||
raise CardDeclined, response
|
||||
end
|
||||
card_uri = response['uri']
|
||||
|
||||
associate_card_to_account(post[:account_uri], card_uri)
|
||||
|
||||
post[:card_uri] = card_uri
|
||||
elsif credit_card.kind_of?(String)
|
||||
post[:card_uri] = credit_card
|
||||
end
|
||||
|
||||
post[:card_uri]
|
||||
end
|
||||
|
||||
def associate_card_to_account(account_uri, card_uri)
|
||||
http_request(:put, account_uri, :card_uri => card_uri)
|
||||
end
|
||||
|
||||
def http_request(method, url, parameters={}, meta={})
|
||||
begin
|
||||
if method == :get
|
||||
raw_response = ssl_get(LIVE_URL + url, headers(meta))
|
||||
else
|
||||
raw_response = ssl_request(method,
|
||||
LIVE_URL + url,
|
||||
post_data(parameters),
|
||||
headers(meta))
|
||||
end
|
||||
parse(raw_response)
|
||||
rescue ResponseError => e
|
||||
raw_response = e.response.body
|
||||
response_error(raw_response)
|
||||
rescue JSON::ParserError
|
||||
json_error(raw_response)
|
||||
end
|
||||
end
|
||||
|
||||
def create_transaction(method, url, parameters, meta={})
|
||||
response = http_request(method, url, parameters, meta)
|
||||
success = !error?(response)
|
||||
|
||||
Response.new(success,
|
||||
(success ? "Transaction approved" : response["description"]),
|
||||
response,
|
||||
:test => (@marketplace_uri.index("TEST") ? true : false),
|
||||
:authorization => response["uri"]
|
||||
)
|
||||
end
|
||||
|
||||
def failed_response(response)
|
||||
is_test = false
|
||||
if @marketplace_uri
|
||||
is_test = (@marketplace_uri.index("TEST") ? true : false)
|
||||
end
|
||||
|
||||
Response.new(false,
|
||||
response["description"],
|
||||
response,
|
||||
:test => is_test
|
||||
)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
JSON.parse(body)
|
||||
end
|
||||
|
||||
def response_error(raw_response)
|
||||
begin
|
||||
parse(raw_response)
|
||||
rescue JSON::ParserError
|
||||
json_error(raw_response)
|
||||
end
|
||||
end
|
||||
|
||||
def json_error(raw_response)
|
||||
msg = 'Invalid response received from the Balanced API. Please contact support@balancedpayments.com if you continue to receive this message.'
|
||||
msg += " (The raw response returned by the API was #{raw_response.inspect})"
|
||||
{
|
||||
"error" => {
|
||||
"message" => msg
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def error?(response)
|
||||
response.key?('status_code')
|
||||
end
|
||||
|
||||
def post_data(params)
|
||||
return nil unless params
|
||||
|
||||
params.map do |key, value|
|
||||
next if value.blank?
|
||||
if value.is_a?(Hash)
|
||||
h = {}
|
||||
value.each do |k, v|
|
||||
h["#{key}[#{k}]"] = v unless v.blank?
|
||||
end
|
||||
post_data(h)
|
||||
else
|
||||
"#{key}=#{CGI.escape(value.to_s)}"
|
||||
end
|
||||
end.compact.join("&")
|
||||
end
|
||||
|
||||
def headers(meta={})
|
||||
@@ua ||= JSON.dump({
|
||||
:bindings_version => ActiveMerchant::VERSION,
|
||||
:lang => 'ruby',
|
||||
:lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
|
||||
:lib_version => BalancedGateway::VERSION,
|
||||
:platform => RUBY_PLATFORM,
|
||||
:publisher => 'active_merchant'
|
||||
})
|
||||
|
||||
{
|
||||
"Authorization" => "Basic " + Base64.encode64(@options[:login].to_s + ":").strip,
|
||||
"User-Agent" => "Balanced/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
|
||||
"X-Balanced-User-Agent" => @@ua,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
105
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/banwire.rb
vendored
Normal file
105
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/banwire.rb
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BanwireGateway < Gateway
|
||||
URL = 'https://banwire.com/api.pago_pro'
|
||||
|
||||
self.supported_countries = ['MX']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express]
|
||||
self.homepage_url = 'http://www.banwire.com/'
|
||||
self.display_name = 'Banwire'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_response_type(post)
|
||||
add_customer_data(post, options)
|
||||
add_order_data(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, creditcard, options)
|
||||
add_customer_data(post, options)
|
||||
add_amount(post, money, options)
|
||||
|
||||
commit(money, post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_response_type(post)
|
||||
post[:response_format] = "JSON"
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:user] = @options[:login]
|
||||
post[:phone] = options[:billing_address][:phone]
|
||||
post[:mail] = options[:email]
|
||||
end
|
||||
|
||||
def add_order_data(post, options)
|
||||
post[:reference] = options[:order_id]
|
||||
post[:concept] = options[:description]
|
||||
end
|
||||
|
||||
def add_address(post, creditcard, options)
|
||||
post[:address] = options[:billing_address][:address1]
|
||||
post[:post_code] = options[:billing_address][:zip]
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:card_num] = creditcard.number
|
||||
post[:card_name] = creditcard.name
|
||||
post[:card_type] = card_brand(creditcard)
|
||||
post[:card_exp] = "#{sprintf("%02d", creditcard.month)}/#{"#{creditcard.year}"[-2, 2]}"
|
||||
post[:card_ccv2] = creditcard.verification_value
|
||||
end
|
||||
|
||||
def add_amount(post, money, options)
|
||||
post[:ammount] = amount(money)
|
||||
post[:currency] = options[:currency]
|
||||
end
|
||||
|
||||
def card_brand(card)
|
||||
brand = super
|
||||
({"master" => "mastercard", "american_express" => "amex"}[brand] || brand)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
JSON.parse(body)
|
||||
end
|
||||
|
||||
def commit(money, parameters)
|
||||
raw_response = ssl_post(URL, post_data(parameters))
|
||||
begin
|
||||
response = parse(raw_response)
|
||||
rescue JSON::ParserError
|
||||
response = json_error(raw_response)
|
||||
end
|
||||
|
||||
Response.new(success?(response),
|
||||
response["message"],
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => response["code_auth"])
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
(response["response"] == "ok")
|
||||
end
|
||||
|
||||
def post_data(parameters = {})
|
||||
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
def json_error(raw_response)
|
||||
msg = 'Invalid response received from the Banwire API. Please contact Banwire support if you continue to receive this message.'
|
||||
msg += " (The raw response returned by the API was #{raw_response.inspect})"
|
||||
{
|
||||
"message" => msg
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
314
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/barclays_epdq.rb
vendored
Normal file
314
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/barclays_epdq.rb
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BarclaysEpdqGateway < Gateway
|
||||
self.test_url = 'https://secure2.mde.epdq.co.uk:11500'
|
||||
self.live_url = 'https://secure2.epdq.co.uk:11500'
|
||||
|
||||
self.supported_countries = ['GB']
|
||||
self.default_currency = 'GBP'
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :maestro, :switch ]
|
||||
self.money_format = :cents
|
||||
self.homepage_url = 'http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/'
|
||||
self.display_name = 'Barclays ePDQ'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :client_id)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
document = Document.new(self, @options) do
|
||||
add_order_form(options[:order_id]) do
|
||||
add_consumer(options) do
|
||||
add_creditcard(creditcard)
|
||||
end
|
||||
add_transaction(:PreAuth, money)
|
||||
end
|
||||
end
|
||||
|
||||
commit(document)
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
# disable fraud checks if this is a repeat order:
|
||||
if options[:payment_number] && (options[:payment_number] > 1)
|
||||
no_fraud = true
|
||||
else
|
||||
no_fraud = options[:no_fraud]
|
||||
end
|
||||
document = Document.new(self, @options, :no_fraud => no_fraud) do
|
||||
add_order_form(options[:order_id], options[:group_id]) do
|
||||
add_consumer(options) do
|
||||
add_creditcard(creditcard)
|
||||
end
|
||||
add_transaction(:Auth, money, options)
|
||||
end
|
||||
end
|
||||
commit(document)
|
||||
end
|
||||
|
||||
# authorization is your unique order ID, not the authorization
|
||||
# code returned by ePDQ
|
||||
def capture(money, authorization, options = {})
|
||||
document = Document.new(self, @options) do
|
||||
add_order_form(authorization) do
|
||||
add_transaction(:PostAuth, money)
|
||||
end
|
||||
end
|
||||
|
||||
commit(document)
|
||||
end
|
||||
|
||||
# authorization is your unique order ID, not the authorization
|
||||
# code returned by ePDQ
|
||||
def credit(money, creditcard_or_authorization, options = {})
|
||||
if creditcard_or_authorization.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, creditcard_or_authorization, options)
|
||||
else
|
||||
credit_new_order(money, creditcard_or_authorization, options)
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
credit_existing_order(money, authorization, options)
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
document = Document.new(self, @options) do
|
||||
add_order_form(authorization) do
|
||||
add_transaction(:Void)
|
||||
end
|
||||
end
|
||||
|
||||
commit(document)
|
||||
end
|
||||
|
||||
private
|
||||
def credit_new_order(money, creditcard, options)
|
||||
document = Document.new(self, @options) do
|
||||
add_order_form do
|
||||
add_consumer(options) do
|
||||
add_creditcard(creditcard)
|
||||
end
|
||||
add_transaction(:Credit, money)
|
||||
end
|
||||
end
|
||||
|
||||
commit(document)
|
||||
end
|
||||
|
||||
def credit_existing_order(money, authorization, options)
|
||||
order_id, _ = authorization.split(":")
|
||||
document = Document.new(self, @options) do
|
||||
add_order_form(order_id) do
|
||||
add_transaction(:Credit, money)
|
||||
end
|
||||
end
|
||||
|
||||
commit(document)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
parser = Parser.new(body)
|
||||
response = parser.parse
|
||||
Response.new(response[:success], response[:message], response,
|
||||
:test => test?,
|
||||
:authorization => response[:authorization],
|
||||
:avs_result => response[:avsresponse],
|
||||
:cvv_result => response[:cvv_result],
|
||||
:order_id => response[:order_id],
|
||||
:raw_response => response[:raw_response]
|
||||
)
|
||||
end
|
||||
|
||||
def commit(document)
|
||||
url = (test? ? self.test_url : self.live_url)
|
||||
data = ssl_post(url, document.to_xml)
|
||||
parse(data)
|
||||
end
|
||||
|
||||
class Parser
|
||||
def initialize(response)
|
||||
@response = response
|
||||
end
|
||||
|
||||
def parse
|
||||
require 'iconv' unless String.method_defined?(:encode)
|
||||
if String.method_defined?(:encode)
|
||||
doc = REXML::Document.new(@response.encode("UTF-8", "ISO-8859-1"))
|
||||
else
|
||||
ic = Iconv.new('UTF-8', 'ISO-8859-1')
|
||||
doc = REXML::Document.new(ic.iconv(@response))
|
||||
end
|
||||
|
||||
auth_type = find(doc, "//Transaction/Type").to_s
|
||||
|
||||
message = find(doc, "//Message/Text")
|
||||
if message.blank?
|
||||
message = find(doc, "//Transaction/CardProcResp/CcReturnMsg")
|
||||
end
|
||||
|
||||
case auth_type
|
||||
when 'Credit', 'Void'
|
||||
success = find(doc, "//CcReturnMsg") == "Approved."
|
||||
else
|
||||
success = find(doc, "//Transaction/AuthCode").present?
|
||||
end
|
||||
|
||||
{
|
||||
:success => success,
|
||||
:message => message,
|
||||
:transaction_id => find(doc, "//Transaction/Id"),
|
||||
:avs_result => find(doc, "//Transaction/AvsRespCode"),
|
||||
:cvv_result => find(doc, "//Transaction/Cvv2Resp"),
|
||||
:authorization => find(doc, "//OrderFormDoc/Id"),
|
||||
:raw_response => @response
|
||||
}
|
||||
end
|
||||
|
||||
def find(doc, xpath)
|
||||
REXML::XPath.first(doc, xpath).try(:text)
|
||||
end
|
||||
end
|
||||
|
||||
class Document
|
||||
attr_reader :type, :xml
|
||||
|
||||
PAYMENT_INTERVALS = {
|
||||
:days => 'D',
|
||||
:months => 'M'
|
||||
}
|
||||
|
||||
EPDQ_CARD_TYPES = {
|
||||
:visa => 1,
|
||||
:master => 2,
|
||||
:switch => 9,
|
||||
:maestro => 10,
|
||||
}
|
||||
|
||||
def initialize(gateway, options = {}, document_options = {}, &block)
|
||||
@gateway = gateway
|
||||
@options = options
|
||||
@document_options = document_options
|
||||
@xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
build(&block)
|
||||
end
|
||||
|
||||
def to_xml
|
||||
@xml.target!
|
||||
end
|
||||
|
||||
def build(&block)
|
||||
xml.instruct!(:xml, :version => '1.0')
|
||||
xml.EngineDocList do
|
||||
xml.DocVersion "1.0"
|
||||
xml.EngineDoc do
|
||||
xml.ContentType "OrderFormDoc"
|
||||
xml.User do
|
||||
xml.Name(@options[:login])
|
||||
xml.Password(@options[:password])
|
||||
xml.ClientId({ :DataType => "S32" }, @options[:client_id])
|
||||
end
|
||||
xml.Instructions do
|
||||
if @document_options[:no_fraud]
|
||||
xml.Pipeline "PaymentNoFraud"
|
||||
else
|
||||
xml.Pipeline "Payment"
|
||||
end
|
||||
end
|
||||
instance_eval(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_order_form(order_id=nil, group_id=nil, &block)
|
||||
xml.OrderFormDoc do
|
||||
xml.Mode 'P'
|
||||
xml.Id(order_id) if order_id
|
||||
xml.GroupId(group_id) if group_id
|
||||
instance_eval(&block)
|
||||
end
|
||||
end
|
||||
|
||||
def add_consumer(options=nil, &block)
|
||||
xml.Consumer do
|
||||
if options
|
||||
xml.Email(options[:email]) if options[:email]
|
||||
billing_address = options[:billing_address] || options[:address]
|
||||
if billing_address
|
||||
xml.BillTo do
|
||||
xml.Location do
|
||||
xml.Address do
|
||||
xml.Street1 billing_address[:address1]
|
||||
xml.Street2 billing_address[:address2]
|
||||
xml.City billing_address[:city]
|
||||
xml.StateProv billing_address[:state]
|
||||
xml.PostalCode billing_address[:zip]
|
||||
xml.Country billing_address[:country_code]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
instance_eval(&block)
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(creditcard)
|
||||
xml.PaymentMech do
|
||||
xml.CreditCard do
|
||||
xml.Type({ :DataType => 'S32' }, EPDQ_CARD_TYPES[creditcard.brand.to_sym])
|
||||
xml.Number creditcard.number
|
||||
xml.Expires({ :DataType => 'ExpirationDate', :Locale => 826 }, format_expiry_date(creditcard))
|
||||
if creditcard.verification_value.present?
|
||||
xml.Cvv2Indicator 1
|
||||
xml.Cvv2Val creditcard.verification_value
|
||||
else
|
||||
xml.Cvv2Indicator 5
|
||||
end
|
||||
xml.IssueNum(creditcard.issue_number) if creditcard.issue_number.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_transaction(auth_type, amount = nil, options = {})
|
||||
@auth_type = auth_type
|
||||
xml.Transaction do
|
||||
xml.Type @auth_type.to_s
|
||||
if options[:payment_number] && options[:payment_number] > 1
|
||||
xml.CardholderPresentCode({ :DataType => 'S32' }, 8)
|
||||
else
|
||||
xml.CardholderPresentCode({ :DataType => 'S32' }, 7)
|
||||
end
|
||||
if options[:payment_number]
|
||||
xml.PaymentNumber({ :DataType => 'S32' }, options[:payment_number])
|
||||
end
|
||||
if options[:total_payments]
|
||||
xml.TotalNumberPayments({ :DataType => 'S32' }, options[:total_payments])
|
||||
end
|
||||
if amount
|
||||
xml.CurrentTotals do
|
||||
xml.Totals do
|
||||
xml.Total({ :DataType => 'Money', :Currency => 826 }, amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# date must be formatted MM/YY
|
||||
def format_expiry_date(creditcard)
|
||||
month_str = "%02d" % creditcard.month
|
||||
if match = creditcard.year.to_s.match(/^\d{2}(\d{2})$/)
|
||||
year_str = "%02d" % match[1].to_i
|
||||
else
|
||||
year_str = "%02d" % creditcard.year
|
||||
end
|
||||
"#{month_str}/#{year_str}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
169
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream.rb
vendored
Normal file
169
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream.rb
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
require File.dirname(__FILE__) + '/beanstream/beanstream_core'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# This class implements the Canadian {Beanstream}[http://www.beanstream.com] payment gateway.
|
||||
# It is also named TD Canada Trust Online Mart payment gateway.
|
||||
# To learn more about the specification of Beanstream gateway, please read the OM_Direct_Interface_API.pdf,
|
||||
# which you can get from your Beanstream account or get from me by email.
|
||||
#
|
||||
# == Supported transaction types by Beanstream:
|
||||
# * +P+ - Purchase
|
||||
# * +PA+ - Pre Authorization
|
||||
# * +PAC+ - Pre Authorization Completion
|
||||
#
|
||||
# == Secure Payment Profiles:
|
||||
# BeanStream supports payment profiles (vaults). This allows you to store cc information with BeanStream and process subsequent transactions with a customer id.
|
||||
# Secure Payment Profiles must be enabled on your account (must be done over the phone).
|
||||
# Your API Access Passcode must be set in Administration => account settings => order settings.
|
||||
# To learn more about storing credit cards with the Beanstream gateway, please read the BEAN_Payment_Profiles.pdf (I had to phone BeanStream to request it.)
|
||||
#
|
||||
# == Notes
|
||||
# * Adding of order products information is not implemented.
|
||||
# * Ensure that country and province data is provided as a code such as "CA", "US", "QC".
|
||||
# * login is the Beanstream merchant ID, username and password should be enabled in your Beanstream account and passed in using the <tt>:user</tt> and <tt>:password</tt> options.
|
||||
# * Test your app with your true merchant id and test credit card information provided in the api pdf document.
|
||||
# * Beanstream does not allow Payment Profiles to be deleted with their API. The accounts are 'closed', but have to be deleted manually.
|
||||
#
|
||||
# Example authorization (Beanstream PA transaction type):
|
||||
#
|
||||
# twenty = 2000
|
||||
# gateway = BeanstreamGateway.new(
|
||||
# :login => '100200000',
|
||||
# :user => 'xiaobozz',
|
||||
# :password => 'password'
|
||||
# )
|
||||
#
|
||||
# credit_card = CreditCard.new(
|
||||
# :number => '4030000010001234',
|
||||
# :month => 8,
|
||||
# :year => 2011,
|
||||
# :first_name => 'xiaobo',
|
||||
# :last_name => 'zzz',
|
||||
# :verification_value => 137
|
||||
# )
|
||||
# response = gateway.authorize(twenty, credit_card,
|
||||
# :order_id => '1234',
|
||||
# :billing_address => {
|
||||
# :name => 'xiaobo zzz',
|
||||
# :phone => '555-555-5555',
|
||||
# :address1 => '1234 Levesque St.',
|
||||
# :address2 => 'Apt B',
|
||||
# :city => 'Montreal',
|
||||
# :state => 'QC',
|
||||
# :country => 'CA',
|
||||
# :zip => 'H2C1X8'
|
||||
# },
|
||||
# :email => 'xiaobozzz@example.com',
|
||||
# :subtotal => 800,
|
||||
# :shipping => 100,
|
||||
# :tax1 => 100,
|
||||
# :tax2 => 100,
|
||||
# :custom => 'reference one'
|
||||
# )
|
||||
class BeanstreamGateway < Gateway
|
||||
include BeanstreamCore
|
||||
|
||||
def authorize(money, source, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_source(post, source)
|
||||
add_address(post, options)
|
||||
add_transaction_type(post, :authorization)
|
||||
add_customer_ip(post, options)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def purchase(money, source, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_source(post, source)
|
||||
add_address(post, options)
|
||||
add_transaction_type(post, purchase_action(source))
|
||||
add_customer_ip(post, options)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
reference, amount, type = split_auth(authorization)
|
||||
|
||||
post = {}
|
||||
add_reference(post, reference)
|
||||
add_original_amount(post, amount)
|
||||
add_transaction_type(post, void_action(type))
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def recurring(money, source, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_credit_card(post, source)
|
||||
add_address(post, options)
|
||||
add_transaction_type(post, purchase_action(source))
|
||||
add_recurring_type(post, options)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def update_recurring(amount, source, options = {})
|
||||
post = {}
|
||||
add_recurring_amount(post, amount)
|
||||
add_recurring_invoice(post, options)
|
||||
add_credit_card(post, source)
|
||||
add_address(post, options)
|
||||
add_recurring_operation_type(post, :update)
|
||||
add_recurring_service(post, options)
|
||||
recurring_commit(post)
|
||||
end
|
||||
|
||||
def cancel_recurring(options = {})
|
||||
post = {}
|
||||
add_recurring_operation_type(post, :cancel)
|
||||
add_recurring_service(post, options)
|
||||
recurring_commit(post)
|
||||
end
|
||||
|
||||
def interac
|
||||
@interac ||= BeanstreamInteracGateway.new(@options)
|
||||
end
|
||||
|
||||
# To match the other stored-value gateways, like TrustCommerce,
|
||||
# store and unstore need to be defined
|
||||
def store(credit_card, options = {})
|
||||
post = {}
|
||||
add_address(post, options)
|
||||
add_credit_card(post, credit_card)
|
||||
add_secure_profile_variables(post,options)
|
||||
commit(post, true)
|
||||
end
|
||||
|
||||
#can't actually delete a secure profile with the supplicaed API. This function sets the status of the profile to closed (C).
|
||||
#Closed profiles will have to removed manually.
|
||||
def delete(vault_id)
|
||||
update(vault_id, false, {:status => "C"})
|
||||
end
|
||||
|
||||
alias_method :unstore, :delete
|
||||
|
||||
# Update the values (such as CC expiration) stored at
|
||||
# the gateway. The CC number must be supplied in the
|
||||
# CreditCard object.
|
||||
def update(vault_id, credit_card, options = {})
|
||||
post = {}
|
||||
add_address(post, options)
|
||||
add_credit_card(post, credit_card)
|
||||
options.merge!({:vault_id => vault_id, :operation => secure_profile_action(:modify)})
|
||||
add_secure_profile_variables(post,options)
|
||||
commit(post, true)
|
||||
end
|
||||
|
||||
private
|
||||
def build_response(*args)
|
||||
Response.new(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
393
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
vendored
Normal file
393
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
module BeanstreamCore
|
||||
RECURRING_URL = 'https://www.beanstream.com/scripts/recurring_billing.asp'
|
||||
SECURE_PROFILE_URL = 'https://www.beanstream.com/scripts/payment_profile.asp'
|
||||
|
||||
SP_SERVICE_VERSION = '1.1'
|
||||
|
||||
TRANSACTIONS = {
|
||||
:authorization => 'PA',
|
||||
:purchase => 'P',
|
||||
:capture => 'PAC',
|
||||
:refund => 'R',
|
||||
:void => 'VP',
|
||||
:check_purchase => 'D',
|
||||
:check_refund => 'C',
|
||||
:void_purchase => 'VP',
|
||||
:void_refund => 'VR'
|
||||
}
|
||||
|
||||
PROFILE_OPERATIONS = {
|
||||
:new => 'N',
|
||||
:modify => 'M'
|
||||
}
|
||||
|
||||
CVD_CODES = {
|
||||
'1' => 'M',
|
||||
'2' => 'N',
|
||||
'3' => 'I',
|
||||
'4' => 'S',
|
||||
'5' => 'U',
|
||||
'6' => 'P'
|
||||
}
|
||||
|
||||
AVS_CODES = {
|
||||
'0' => 'R',
|
||||
'5' => 'I',
|
||||
'9' => 'I'
|
||||
}
|
||||
|
||||
PERIODS = {
|
||||
:days => 'D',
|
||||
:weeks => 'W',
|
||||
:months => 'M',
|
||||
:years => 'Y'
|
||||
}
|
||||
|
||||
PERIODICITIES = {
|
||||
:daily => [:days, 1],
|
||||
:weekly => [:weeks, 1],
|
||||
:biweekly => [:weeks, 2],
|
||||
:monthly => [:months, 1],
|
||||
:bimonthly => [:months, 2],
|
||||
:yearly => [:years, 1]
|
||||
}
|
||||
|
||||
RECURRING_OPERATION = {
|
||||
:update => 'M',
|
||||
:cancel => 'C'
|
||||
}
|
||||
|
||||
def self.included(base)
|
||||
base.default_currency = 'CAD'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
base.supported_countries = ['CA']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
base.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
base.homepage_url = 'http://www.beanstream.com/'
|
||||
base.live_url = 'https://www.beanstream.com/scripts/process_transaction.asp'
|
||||
|
||||
# The name of the gateway
|
||||
base.display_name = 'Beanstream.com'
|
||||
end
|
||||
|
||||
# Only <tt>:login</tt> is required by default,
|
||||
# which is the merchant's merchant ID. If you'd like to perform void,
|
||||
# capture or refund transactions then you'll also need to add a username
|
||||
# and password to your account under administration -> account settings ->
|
||||
# order settings -> Use username/password validation
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
reference, amount, type = split_auth(authorization)
|
||||
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_reference(post, reference)
|
||||
add_transaction_type(post, :capture)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def refund(money, source, options = {})
|
||||
post = {}
|
||||
reference, amount, type = split_auth(source)
|
||||
add_reference(post, reference)
|
||||
add_transaction_type(post, refund_action(type))
|
||||
add_amount(post, money)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
def credit(money, source, options = {})
|
||||
deprecated Gateway::CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, source, options)
|
||||
end
|
||||
|
||||
private
|
||||
def purchase_action(source)
|
||||
if source.is_a?(Check)
|
||||
:check_purchase
|
||||
else
|
||||
:purchase
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_ip(post, options)
|
||||
post[:customerIP] = options[:ip] if options[:ip]
|
||||
end
|
||||
|
||||
def void_action(original_transaction_type)
|
||||
(original_transaction_type == TRANSACTIONS[:refund]) ? :void_refund : :void_purchase
|
||||
end
|
||||
|
||||
def refund_action(type)
|
||||
(type == TRANSACTIONS[:check_purchase]) ? :check_refund : :refund
|
||||
end
|
||||
|
||||
def secure_profile_action(type)
|
||||
PROFILE_OPERATIONS[type] || PROFILE_OPERATIONS[:new]
|
||||
end
|
||||
|
||||
def split_auth(string)
|
||||
string.split(";")
|
||||
end
|
||||
|
||||
def add_amount(post, money)
|
||||
post[:trnAmount] = amount(money)
|
||||
end
|
||||
|
||||
def add_original_amount(post, amount)
|
||||
post[:trnAmount] = amount
|
||||
end
|
||||
|
||||
def add_reference(post, reference)
|
||||
post[:adjId] = reference
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
prepare_address_for_non_american_countries(options)
|
||||
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
post[:ordName] = billing_address[:name]
|
||||
post[:ordEmailAddress] = options[:email]
|
||||
post[:ordPhoneNumber] = billing_address[:phone]
|
||||
post[:ordAddress1] = billing_address[:address1]
|
||||
post[:ordAddress2] = billing_address[:address2]
|
||||
post[:ordCity] = billing_address[:city]
|
||||
post[:ordProvince] = billing_address[:state]
|
||||
post[:ordPostalCode] = billing_address[:zip]
|
||||
post[:ordCountry] = billing_address[:country]
|
||||
end
|
||||
if shipping_address = options[:shipping_address]
|
||||
post[:shipName] = shipping_address[:name]
|
||||
post[:shipEmailAddress] = options[:email]
|
||||
post[:shipPhoneNumber] = shipping_address[:phone]
|
||||
post[:shipAddress1] = shipping_address[:address1]
|
||||
post[:shipAddress2] = shipping_address[:address2]
|
||||
post[:shipCity] = shipping_address[:city]
|
||||
post[:shipProvince] = shipping_address[:state]
|
||||
post[:shipPostalCode] = shipping_address[:zip]
|
||||
post[:shipCountry] = shipping_address[:country]
|
||||
post[:shippingMethod] = shipping_address[:shipping_method]
|
||||
post[:deliveryEstimate] = shipping_address[:delivery_estimate]
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_address_for_non_american_countries(options)
|
||||
[ options[:billing_address], options[:shipping_address] ].compact.each do |address|
|
||||
unless ['US', 'CA'].include?(address[:country])
|
||||
address[:state] = '--'
|
||||
address[:zip] = '000000' unless address[:zip]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:trnOrderNumber] = options[:order_id]
|
||||
post[:trnComments] = options[:description]
|
||||
post[:ordItemPrice] = amount(options[:subtotal])
|
||||
post[:ordShippingPrice] = amount(options[:shipping])
|
||||
post[:ordTax1Price] = amount(options[:tax1] || options[:tax])
|
||||
post[:ordTax2Price] = amount(options[:tax2])
|
||||
post[:ref1] = options[:custom]
|
||||
end
|
||||
|
||||
def add_credit_card(post, credit_card)
|
||||
if credit_card
|
||||
post[:trnCardOwner] = credit_card.name
|
||||
post[:trnCardNumber] = credit_card.number
|
||||
post[:trnExpMonth] = format(credit_card.month, :two_digits)
|
||||
post[:trnExpYear] = format(credit_card.year, :two_digits)
|
||||
post[:trnCardCvd] = credit_card.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def add_check(post, check)
|
||||
# The institution number of the consumer’s financial institution. Required for Canadian dollar EFT transactions.
|
||||
post[:institutionNumber] = check.institution_number
|
||||
|
||||
# The bank transit number of the consumer’s bank account. Required for Canadian dollar EFT transactions.
|
||||
post[:transitNumber] = check.transit_number
|
||||
|
||||
# The routing number of the consumer’s bank account. Required for US dollar EFT transactions.
|
||||
post[:routingNumber] = check.routing_number
|
||||
|
||||
# The account number of the consumer’s bank account. Required for both Canadian and US dollar EFT transactions.
|
||||
post[:accountNumber] = check.account_number
|
||||
end
|
||||
|
||||
def add_secure_profile_variables(post, options = {})
|
||||
post[:serviceVersion] = SP_SERVICE_VERSION
|
||||
post[:responseFormat] = 'QS'
|
||||
post[:cardValidation] = (options[:cardValidation].to_i == 1) || '0'
|
||||
|
||||
post[:operationType] = options[:operationType] || options[:operation] || secure_profile_action(:new)
|
||||
post[:customerCode] = options[:billing_id] || options[:vault_id] || false
|
||||
post[:status] = options[:status]
|
||||
end
|
||||
|
||||
def add_recurring_amount(post, money)
|
||||
post[:amount] = amount(money)
|
||||
end
|
||||
|
||||
def add_recurring_invoice(post, options)
|
||||
post[:rbApplyTax1] = options[:apply_tax1]
|
||||
post[:rbApplyTax2] = options[:apply_tax2]
|
||||
end
|
||||
|
||||
def add_recurring_operation_type(post, operation)
|
||||
post[:operationType] = RECURRING_OPERATION[operation]
|
||||
end
|
||||
|
||||
def add_recurring_service(post, options)
|
||||
post[:serviceVersion] = '1.0'
|
||||
post[:merchantId] = @options[:login]
|
||||
post[:passCode] = @options[:recurring_api_key]
|
||||
post[:rbAccountId] = options[:account_id]
|
||||
end
|
||||
|
||||
def add_recurring_type(post, options)
|
||||
# XXX requires!
|
||||
post[:trnRecurring] = 1
|
||||
period, increment = interval(options)
|
||||
post[:rbBillingPeriod] = PERIODS[period]
|
||||
post[:rbBillingIncrement] = increment
|
||||
|
||||
if options.include? :start_date
|
||||
post[:rbCharge] = 0
|
||||
post[:rbFirstBilling] = options[:start_date].strftime('%m%d%Y')
|
||||
end
|
||||
|
||||
if count = options[:occurrences] || options[:payments]
|
||||
post[:rbExpiry] = (options[:start_date] || Date.current).advance(period => count).strftime('%m%d%Y')
|
||||
end
|
||||
end
|
||||
|
||||
def interval(options)
|
||||
if options.include? :periodicity
|
||||
requires!(options, [:periodicity, *PERIODICITIES.keys])
|
||||
PERIODICITIES[options[:periodicity]]
|
||||
elsif options.include? :interval
|
||||
interval = options[:interval]
|
||||
if interval.respond_to? :parts
|
||||
parts = interval.parts
|
||||
raise ArgumentError.new("Cannot recur with mixed interval (#{interval}). Use only one of: days, weeks, months or years") if parts.length > 1
|
||||
parts.first
|
||||
elsif interval.kind_of? Hash
|
||||
requires!(interval, :unit)
|
||||
unit, length = interval.values_at(:unit, :length)
|
||||
length ||= 1
|
||||
[unit, length]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
results = {}
|
||||
if !body.nil?
|
||||
body.split(/&/).each do |pair|
|
||||
key, val = pair.split(/\=/)
|
||||
results[key.to_sym] = val.nil? ? nil : CGI.unescape(val)
|
||||
end
|
||||
end
|
||||
|
||||
# Clean up the message text if there is any
|
||||
if results[:messageText]
|
||||
results[:messageText].gsub!(/<LI>/, "")
|
||||
results[:messageText].gsub!(/(\.)?<br>/, ". ")
|
||||
results[:messageText].strip!
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def recurring_parse(data)
|
||||
REXML::Document.new(data).root.elements.to_a.inject({}) do |response, element|
|
||||
response[element.name.to_sym] = element.text
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
def commit(params, use_profile_api = false)
|
||||
post(post_data(params,use_profile_api),use_profile_api)
|
||||
end
|
||||
|
||||
def recurring_commit(params)
|
||||
recurring_post(post_data(params, false))
|
||||
end
|
||||
|
||||
def post(data, use_profile_api=nil)
|
||||
response = parse(ssl_post((use_profile_api ? SECURE_PROFILE_URL : self.live_url), data))
|
||||
response[:customer_vault_id] = response[:customerCode] if response[:customerCode]
|
||||
build_response(success?(response), message_from(response), response,
|
||||
:test => test? || response[:authCode] == "TEST",
|
||||
:authorization => authorization_from(response),
|
||||
:cvv_result => CVD_CODES[response[:cvdId]],
|
||||
:avs_result => { :code => (AVS_CODES.include? response[:avsId]) ? AVS_CODES[response[:avsId]] : response[:avsId] }
|
||||
)
|
||||
end
|
||||
|
||||
def recurring_post(data)
|
||||
response = recurring_parse(ssl_post(RECURRING_URL, data))
|
||||
build_response(recurring_success?(response), recurring_message_from(response), response)
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
"#{response[:trnId]};#{response[:trnAmount]};#{response[:trnType]}"
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
response[:messageText] || response[:responseMessage]
|
||||
end
|
||||
|
||||
def recurring_message_from(response)
|
||||
response[:message]
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response[:responseType] == 'R' || response[:trnApproved] == '1' || response[:responseCode] == '1'
|
||||
end
|
||||
|
||||
def recurring_success?(response)
|
||||
response[:code] == '1'
|
||||
end
|
||||
|
||||
def add_source(post, source)
|
||||
if source.is_a?(String) or source.is_a?(Integer)
|
||||
post[:customerCode] = source
|
||||
else
|
||||
card_brand(source) == "check" ? add_check(post, source) : add_credit_card(post, source)
|
||||
end
|
||||
end
|
||||
|
||||
def add_transaction_type(post, action)
|
||||
post[:trnType] = TRANSACTIONS[action]
|
||||
end
|
||||
|
||||
def post_data(params, use_profile_api)
|
||||
params[:requestType] = 'BACKEND'
|
||||
if use_profile_api
|
||||
params[:merchantId] = @options[:login]
|
||||
params[:passCode] = @options[:secure_profile_api_key]
|
||||
else
|
||||
params[:username] = @options[:user] if @options[:user]
|
||||
params[:password] = @options[:password] if @options[:password]
|
||||
params[:merchant_id] = @options[:login]
|
||||
end
|
||||
params[:vbvEnabled] = '0'
|
||||
params[:scEnabled] = '0'
|
||||
|
||||
params.reject{|k, v| v.blank?}.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
54
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream_interac.rb
vendored
Normal file
54
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/beanstream_interac.rb
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
require File.dirname(__FILE__) + '/beanstream/beanstream_core'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BeanstreamInteracResponse < Response
|
||||
def redirect
|
||||
params['pageContents']
|
||||
end
|
||||
end
|
||||
|
||||
class BeanstreamInteracGateway < Gateway
|
||||
include BeanstreamCore
|
||||
|
||||
# Confirm a transaction posted back from the bank to Beanstream.
|
||||
# Confirming a transaction does not require any credentials,
|
||||
# and in an application with many merchants sharing a funded
|
||||
# URL the application may not yet know which merchant the
|
||||
# post back is for until the response of the confirmation is
|
||||
# received, which contains the order number.
|
||||
def self.confirm(transaction)
|
||||
gateway = new(:login => '')
|
||||
gateway.confirm(transaction)
|
||||
end
|
||||
|
||||
def purchase(money, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_address(post, options)
|
||||
add_interac_details(post, options)
|
||||
add_transaction_type(post, :purchase)
|
||||
commit(post)
|
||||
end
|
||||
|
||||
# Confirm a transaction posted back from the bank to Beanstream.
|
||||
def confirm(transaction)
|
||||
post(transaction)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_interac_details(post, options)
|
||||
address = options[:billing_address] || options[:address] || {}
|
||||
post[:trnCardOwner] = address[:name]
|
||||
post[:paymentMethod] = 'IO'
|
||||
end
|
||||
|
||||
def build_response(*args)
|
||||
BeanstreamInteracResponse.new(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
503
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/blue_pay.rb
vendored
Normal file
503
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/blue_pay.rb
vendored
Normal file
@@ -0,0 +1,503 @@
|
||||
require 'digest/md5'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BluePayGateway < Gateway
|
||||
class_attribute :rebilling_url, :ignore_http_status
|
||||
|
||||
self.live_url = 'https://secure.bluepay.com/interfaces/bp20post'
|
||||
self.rebilling_url = 'https://secure.bluepay.com/interfaces/bp20rebadmin'
|
||||
|
||||
self.ignore_http_status = true
|
||||
|
||||
CARD_CODE_ERRORS = %w( N S )
|
||||
AVS_ERRORS = %w( A E N R W Z )
|
||||
AVS_REASON_CODES = %w(27 45)
|
||||
|
||||
FRAUD_REVIEW_STATUSES = %w( E 0 )
|
||||
|
||||
FIELD_MAP = {
|
||||
'TRANS_ID' => :transaction_id,
|
||||
'STATUS' => :response_code,
|
||||
'AVS' => :avs_result_code,
|
||||
'CVV2'=> :card_code,
|
||||
'AUTH_CODE' => :authorization,
|
||||
'MESSAGE' => :message,
|
||||
'REBID' => :rebid,
|
||||
'TRANS_TYPE' => :trans_type,
|
||||
'PAYMENT_ACCOUNT_MASK' => :acct_mask,
|
||||
'CARD_TYPE' => :card_type,
|
||||
}
|
||||
|
||||
REBILL_FIELD_MAP = {
|
||||
'REBILL_ID' => :rebill_id,
|
||||
'ACCOUNT_ID'=> :account_id,
|
||||
'USER_ID' => :user_id,
|
||||
'TEMPLATE_ID' => :template_id,
|
||||
'STATUS' => :status,
|
||||
'CREATION_DATE' => :creation_date,
|
||||
'NEXT_DATE' => :next_date,
|
||||
'LAST_DATE' => :last_date,
|
||||
'SCHED_EXPR' => :schedule,
|
||||
'CYCLES_REMAIN' => :cycles_remain,
|
||||
'REB_AMOUNT' => :rebill_amount,
|
||||
'NEXT_AMOUNT' => :next_amount,
|
||||
'USUAL_DATE' => :undoc_usual_date, # Not found in the bp20rebadmin API doc.
|
||||
}
|
||||
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
||||
self.homepage_url = 'http://www.bluepay.com/'
|
||||
self.display_name = 'BluePay'
|
||||
self.money_format = :dollars
|
||||
|
||||
# Creates a new BluepayGateway
|
||||
#
|
||||
# The gateway requires that a valid Account ID and Secret Key be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:account_id</tt> -- The BluePay gateway Account ID (REQUIRED)
|
||||
# * <tt>:secret_key</tt> -- The BluePay gateway Secret Key (REQUIRED)
|
||||
# * <tt>:test</tt> -- set to true for TEST mode or false for LIVE mode
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Performs an authorization, which reserves the funds on the customer's credit card. This does not actually take funds from the customer
|
||||
# This is referred to an AUTH transaction in BluePay
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>payment_object</tt> -- This can either be one of three things:
|
||||
# A CreditCard object,
|
||||
# A Check object,
|
||||
# or a token. The token is called the Master ID. This is a unique transaction ID returned from a previous transaction. This token associates all the stored information for a previous transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
def authorize(money, payment_object, options = {})
|
||||
post = {}
|
||||
add_payment_method(post, payment_object)
|
||||
add_invoice(post, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
add_rebill(post, options) if options[:rebill]
|
||||
add_duplicate_override(post, options)
|
||||
post[:TRANS_TYPE] = 'AUTH'
|
||||
commit('AUTH_ONLY', money, post)
|
||||
end
|
||||
|
||||
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
||||
# This is referred to a SALE transaction in BluePay
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
||||
# * <tt>payment_object</tt> -- This can either be one of three things:
|
||||
# A CreditCard object,
|
||||
# A Check object,
|
||||
# or a token. The token is called the Master ID. This is a unique transaction ID returned from a previous transaction. This token associates all the stored information for a previous transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.,
|
||||
def purchase(money, payment_object, options = {})
|
||||
post = {}
|
||||
add_payment_method(post, payment_object)
|
||||
add_invoice(post, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
add_rebill(post, options) if options[:rebill]
|
||||
add_duplicate_override(post, options)
|
||||
post[:TRANS_TYPE] = 'SALE'
|
||||
commit('AUTH_CAPTURE', money, post)
|
||||
end
|
||||
|
||||
# Captures the funds from an authorize transaction.
|
||||
# This is referred to a CAPTURE transaction in BluePay
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
||||
# * <tt>identification</tt> -- The Master ID, or token, returned from the previous authorize transaction.
|
||||
def capture(money, identification, options = {})
|
||||
post = {}
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
post[:MASTER_ID] = identification
|
||||
post[:TRANS_TYPE] = 'CAPTURE'
|
||||
commit('PRIOR_AUTH_CAPTURE', money, post)
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
# This is referred to a VOID transaction in BluePay
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>identification</tt> - The Master ID, or token, returned from a previous authorize transaction.
|
||||
def void(identification, options = {})
|
||||
post = {}
|
||||
post[:MASTER_ID] = identification
|
||||
post[:TRANS_TYPE] = 'VOID'
|
||||
commit('VOID', nil, post)
|
||||
end
|
||||
|
||||
# Performs a credit.
|
||||
#
|
||||
# This transaction indicates that money should flow from the merchant to the customer.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
||||
# * <tt>payment_object</tt> -- This can either be one of three things:
|
||||
# A CreditCard object,
|
||||
# A Check object,
|
||||
# or a token. The token is called the Master ID. This is a unique transaction ID returned from a previous transaction. This token associates all the stored information for a previous transaction.
|
||||
# If the payment_object is a token, then the transaction type will reverse a previous capture or purchase transaction, returning the funds to the customer. If the amount is nil, a full credit will be processed. This is referred to a REFUND transaction in BluePay.
|
||||
# If the payment_object is either a CreditCard or Check object, then the transaction type will be an unmatched credit placing funds in the specified account. This is referred to a CREDIT transaction in BluePay.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
def refund(money, identification, options = {})
|
||||
if(identification && !identification.kind_of?(String))
|
||||
deprecated "refund should only be used to refund a referenced transaction"
|
||||
return credit(money, identification, options)
|
||||
end
|
||||
|
||||
post = {}
|
||||
post[:PAYMENT_ACCOUNT] = ''
|
||||
post[:MASTER_ID] = identification
|
||||
post[:TRANS_TYPE] = 'REFUND'
|
||||
post[:NAME1] = (options[:first_name] ? options[:first_name] : "")
|
||||
post[:NAME2] = options[:last_name] if options[:last_name]
|
||||
post[:ZIP] = options[:zip] if options[:zip]
|
||||
add_invoice(post, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('CREDIT', money, post)
|
||||
end
|
||||
|
||||
def credit(money, payment_object, options = {})
|
||||
if(payment_object && payment_object.kind_of?(String))
|
||||
deprecated "credit should only be used to credit a payment method"
|
||||
return refund(money, payment_object, options)
|
||||
end
|
||||
|
||||
post = {}
|
||||
post[:PAYMENT_ACCOUNT] = ''
|
||||
add_payment_method(post, payment_object)
|
||||
post[:TRANS_TYPE] = 'CREDIT'
|
||||
|
||||
post[:NAME1] = (options[:first_name] ? options[:first_name] : "")
|
||||
post[:NAME2] = options[:last_name] if options[:last_name]
|
||||
post[:ZIP] = options[:zip] if options[:zip]
|
||||
add_invoice(post, options)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('CREDIT', money, post)
|
||||
end
|
||||
|
||||
# Create a new recurring payment.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to charge the customer at the time of the recurring payment setup, in cents. Set to zero if you do not want the customer to be charged at this time.
|
||||
# * <tt>payment_object</tt> -- This can either be one of three things:
|
||||
# A CreditCard object,
|
||||
# A Check object,
|
||||
# or a token. The token is called the Master ID. This is a unique transaction ID returned from a previous transaction. This token associates all the stored information for a previous transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.,
|
||||
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:rebill_start_date</tt> is a string that tells the gateway when to start the rebill. (REQUIRED)
|
||||
# Has two valid formats:
|
||||
# "YYYY-MM-DD HH:MM:SS" Hours, minutes, and seconds are optional.
|
||||
# "XX UNITS" Relative date as explained below. Marked from the time of the
|
||||
# transaction (i.e.: 10 DAYS, 1 MONTH, 1 YEAR)
|
||||
# * <tt>:rebill_expression</tt> is the period of time in-between rebillings. (REQUIRED)
|
||||
# It uses the same "XX UNITS" format as rebill_start_date, explained above.
|
||||
# Optional parameters include:
|
||||
# * <tt>rebill_cycles</tt>: Number of times to rebill. Don't send or set to nil for infinite rebillings (or
|
||||
# until canceled).
|
||||
# * <tt>rebill_amount</tt>: Amount to rebill. Defaults to amount of transaction for rebillings.
|
||||
#
|
||||
# For example, to charge the customer $19.95 now and then charge $39.95 in 60 days every 3 months for 5 times, the options hash would be as follows:
|
||||
# :rebill_start_date => '60 DAYS',
|
||||
# :rebill_expression => '3 MONTHS',
|
||||
# :rebill_cycles => '5',
|
||||
# :rebill_amount => '39.95'
|
||||
# A money object of 1995 cents would be passed into the 'money' parameter.
|
||||
def recurring(money, payment_object, options = {})
|
||||
requires!(options, :rebill_start_date, :rebill_expression)
|
||||
options[:rebill] = true
|
||||
if money
|
||||
purchase(money, payment_object, options)
|
||||
else
|
||||
authorize(money, payment_object, options)
|
||||
end
|
||||
end
|
||||
|
||||
# View a recurring payment
|
||||
#
|
||||
# This will pull data associated with a current recurring billing
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>rebill_id</tt> -- A string containing the rebill_id of the recurring billing that is already active (REQUIRED)
|
||||
def status_recurring(rebill_id)
|
||||
post = {}
|
||||
requires!(rebill_id)
|
||||
post[:REBILL_ID] = rebill_id
|
||||
post[:TRANS_TYPE] = 'GET'
|
||||
commit('rebill', 'nil', post)
|
||||
end
|
||||
|
||||
# Update a recurring payment's details.
|
||||
#
|
||||
# This transaction updates an existing recurring billing
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:rebill_id</tt> -- The 12 digit rebill ID used to update a particular rebilling cycle. (REQUIRED)
|
||||
# * <tt>:rebill_amount</tt> -- A string containing the new rebilling amount.
|
||||
# * <tt>:rebill_next_date</tt> -- A string containing the new rebilling next date.
|
||||
# * <tt>:rebill_expression</tt> -- A string containing the new rebilling expression.
|
||||
# * <tt>:rebill_cycles</tt> -- A string containing the new rebilling cycles.
|
||||
# * <tt>:rebill_next_amount</tt> -- A string containing the next rebilling amount to charge the customer. This ONLY affects the next scheduled charge; all other rebillings will continue at the regular (rebill_amount) amount.
|
||||
# Take a look above at the recurring_payment method for similar examples on how to use.
|
||||
def update_recurring(options = {})
|
||||
post = {}
|
||||
requires!(options, :rebill_id)
|
||||
post[:REBILL_ID] = options[:rebill_id]
|
||||
post[:TRANS_TYPE] = 'SET'
|
||||
post[:REB_AMOUNT] = amount(options[:rebill_amount]) if options[:rebill_amount]
|
||||
post[:NEXT_DATE] = options[:rebill_next_date]
|
||||
post[:REB_EXPR] = options[:rebill_expression]
|
||||
post[:REB_CYCLES] = options[:rebill_cycles]
|
||||
post[:NEXT_AMOUNT] = options[:rebill_next_amount]
|
||||
commit('rebill', 'nil', post)
|
||||
end
|
||||
|
||||
# Cancel a recurring payment.
|
||||
#
|
||||
# This transaction cancels an existing recurring billing.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>rebill_id</tt> -- A string containing the rebill_id of the recurring billing that you wish to cancel/stop (REQUIRED)
|
||||
def cancel_recurring(rebill_id)
|
||||
post = {}
|
||||
requires!(rebill_id)
|
||||
post[:REBILL_ID] = rebill_id
|
||||
post[:TRANS_TYPE] = 'SET'
|
||||
post[:STATUS] = 'stopped'
|
||||
commit('rebill', 'nil', post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit(action, money, fields)
|
||||
fields[:AMOUNT] = amount(money) unless(fields[:TRANS_TYPE] == 'VOID' || action == 'rebill')
|
||||
fields[:MODE] = (test? ? 'TEST' : 'LIVE')
|
||||
fields[:ACCOUNT_ID] = @options[:login]
|
||||
|
||||
if action == 'rebill'
|
||||
url = rebilling_url
|
||||
fields[:TAMPER_PROOF_SEAL] = calc_rebill_tps(fields)
|
||||
else
|
||||
url = live_url
|
||||
fields[:TAMPER_PROOF_SEAL] = calc_tps(amount(money), fields)
|
||||
end
|
||||
parse(ssl_post(url, post_data(action, fields)))
|
||||
end
|
||||
|
||||
def parse_recurring(response_fields, opts={}) # expected status?
|
||||
parsed = {}
|
||||
response_fields.each do |k,v|
|
||||
mapped_key = REBILL_FIELD_MAP.include?(k) ? REBILL_FIELD_MAP[k] : k
|
||||
parsed[mapped_key] = v
|
||||
end
|
||||
|
||||
success = parsed[:status] != 'error'
|
||||
message = parsed[:status]
|
||||
|
||||
Response.new(success, message, parsed,
|
||||
:test => test?,
|
||||
:authorization => parsed[:rebill_id])
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
# The bp20api has max one value per form field.
|
||||
response_fields = Hash[CGI::parse(body).map{|k,v| [k.upcase,v.first]}]
|
||||
|
||||
if response_fields.include? "REBILL_ID"
|
||||
return parse_recurring(response_fields)
|
||||
end
|
||||
|
||||
parsed = {}
|
||||
response_fields.each do |k,v|
|
||||
mapped_key = FIELD_MAP.include?(k) ? FIELD_MAP[k] : k
|
||||
parsed[mapped_key] = v
|
||||
end
|
||||
|
||||
# normalize message
|
||||
message = message_from(parsed)
|
||||
success = parsed[:response_code] == '1'
|
||||
Response.new(success, message, parsed,
|
||||
:test => test?,
|
||||
:authorization => (parsed[:rebid] && parsed[:rebid] != '' ? parsed[:rebid] : parsed[:transaction_id]),
|
||||
:fraud_review => FRAUD_REVIEW_STATUSES.include?(parsed[:response_code]),
|
||||
:avs_result => { :code => parsed[:avs_result_code] },
|
||||
:cvv_result => parsed[:card_code]
|
||||
)
|
||||
end
|
||||
|
||||
def message_from(parsed)
|
||||
message = parsed[:message]
|
||||
if(parsed[:response_code].to_i == 2)
|
||||
if CARD_CODE_ERRORS.include?(parsed[:card_code])
|
||||
message = CVVResult.messages[parsed[:card_code]]
|
||||
elsif AVS_ERRORS.include?(parsed[:avs_result_code])
|
||||
message = AVSResult.messages[ parsed[:avs_result_code] ]
|
||||
else
|
||||
message = message.chomp('.')
|
||||
end
|
||||
elsif message == "Missing ACCOUNT_ID"
|
||||
message = "The merchant login ID or password is invalid"
|
||||
elsif message =~ /Approved/
|
||||
message = "This transaction has been approved"
|
||||
elsif message =~ /Expired/
|
||||
message = "The credit card has expired"
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:ORDER_ID] = options[:order_id]
|
||||
post[:INVOICE_ID] = options[:invoice]
|
||||
post[:invoice_num] = options[:order_id]
|
||||
post[:MEMO] = options[:description]
|
||||
post[:description] = options[:description]
|
||||
end
|
||||
|
||||
def add_payment_method(post, payment_object)
|
||||
post[:MASTER_ID] = ''
|
||||
case payment_object
|
||||
when String
|
||||
post[:MASTER_ID] = payment_object
|
||||
when Check
|
||||
add_check(post, payment_object)
|
||||
else
|
||||
add_creditcard(post, payment_object)
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:PAYMENT_TYPE] = 'CREDIT'
|
||||
post[:PAYMENT_ACCOUNT] = creditcard.number
|
||||
post[:CARD_CVV2] = creditcard.verification_value
|
||||
post[:CARD_EXPIRE] = expdate(creditcard)
|
||||
post[:NAME1] = creditcard.first_name
|
||||
post[:NAME2] = creditcard.last_name
|
||||
end
|
||||
|
||||
CHECK_ACCOUNT_TYPES = {
|
||||
"checking" => "C",
|
||||
"savings" => "S"
|
||||
}
|
||||
|
||||
def add_check(post, check)
|
||||
post[:PAYMENT_TYPE] = 'ACH'
|
||||
post[:PAYMENT_ACCOUNT] = [CHECK_ACCOUNT_TYPES[check.account_type], check.routing_number, check.account_number].join(":")
|
||||
post[:NAME1] = check.first_name
|
||||
post[:NAME2] = check.last_name
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:EMAIL] = options[:email]
|
||||
post[:CUSTOM_ID] = options[:customer]
|
||||
end
|
||||
|
||||
def add_duplicate_override(post, options)
|
||||
post[:DUPLICATE_OVERRIDE] = options[:duplicate_override]
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = (options[:shipping_address] || options[:billing_address] || options[:address])
|
||||
post[:NAME1] = address[:first_name]
|
||||
post[:NAME2] = address[:last_name]
|
||||
post[:ADDR1] = address[:address1]
|
||||
post[:ADDR2] = address[:address2]
|
||||
post[:COMPANY_NAME] = address[:company]
|
||||
post[:PHONE] = address[:phone]
|
||||
post[:CITY] = address[:city]
|
||||
post[:STATE] = (address[:state].blank? ? 'n/a' : address[:state])
|
||||
post[:ZIP] = address[:zip]
|
||||
post[:COUNTRY] = address[:country]
|
||||
end
|
||||
end
|
||||
|
||||
def add_rebill(post, options)
|
||||
post[:DO_REBILL] = '1'
|
||||
post[:REB_AMOUNT] = amount(options[:rebill_amount])
|
||||
post[:REB_FIRST_DATE] = options[:rebill_start_date]
|
||||
post[:REB_EXPR] = options[:rebill_expression]
|
||||
post[:REB_CYCLES] = options[:rebill_cycles]
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {}
|
||||
post[:version] = '1'
|
||||
post[:login] = ''
|
||||
post[:tran_key] = ''
|
||||
post[:relay_response] = "FALSE"
|
||||
post[:type] = action
|
||||
post[:delim_data] = "TRUE"
|
||||
post[:delim_char] = ","
|
||||
post[:encap_char] = "$"
|
||||
post[:card_num] = '4111111111111111'
|
||||
post[:exp_date] = '1212'
|
||||
post[:solution_ID] = application_id if(application_id && application_id != "ActiveMerchant")
|
||||
post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = format(creditcard.year, :two_digits)
|
||||
month = format(creditcard.month, :two_digits)
|
||||
|
||||
"#{month}#{year}"
|
||||
end
|
||||
|
||||
def calc_tps(amount, post)
|
||||
post[:NAME1] ||= ''
|
||||
Digest::MD5.hexdigest(
|
||||
[
|
||||
@options[:password],
|
||||
@options[:login],
|
||||
post[:TRANS_TYPE],
|
||||
amount,
|
||||
post[:MASTER_ID],
|
||||
post[:NAME1],
|
||||
post[:PAYMENT_ACCOUNT]
|
||||
].join("")
|
||||
)
|
||||
end
|
||||
|
||||
def calc_rebill_tps(post)
|
||||
Digest::MD5.hexdigest(
|
||||
[
|
||||
@options[:password],
|
||||
@options[:login],
|
||||
post[:TRANS_TYPE],
|
||||
post[:REBILL_ID]
|
||||
].join("")
|
||||
)
|
||||
end
|
||||
|
||||
def handle_response(response)
|
||||
if ignore_http_status || (200...300).include?(response.code.to_i)
|
||||
return response.body
|
||||
end
|
||||
raise ResponseError.new(response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
142
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/bogus.rb
vendored
Normal file
142
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/bogus.rb
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# Bogus Gateway
|
||||
class BogusGateway < Gateway
|
||||
AUTHORIZATION = '53433'
|
||||
|
||||
SUCCESS_MESSAGE = "Bogus Gateway: Forced success"
|
||||
FAILURE_MESSAGE = "Bogus Gateway: Forced failure"
|
||||
ERROR_MESSAGE = "Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error"
|
||||
CREDIT_ERROR_MESSAGE = "Bogus Gateway: Use CreditCard number ending in 1 for success, 2 for exception and anything else for error"
|
||||
UNSTORE_ERROR_MESSAGE = "Bogus Gateway: Use trans_id ending in 1 for success, 2 for exception and anything else for error"
|
||||
CAPTURE_ERROR_MESSAGE = "Bogus Gateway: Use authorization number ending in 1 for exception, 2 for error and anything else for success"
|
||||
VOID_ERROR_MESSAGE = "Bogus Gateway: Use authorization number ending in 1 for exception, 2 for error and anything else for success"
|
||||
REFUND_ERROR_MESSAGE = "Bogus Gateway: Use trans_id number ending in 1 for exception, 2 for error and anything else for success"
|
||||
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:bogus]
|
||||
self.homepage_url = 'http://example.com'
|
||||
self.display_name = 'Bogus'
|
||||
|
||||
def authorize(money, credit_card_or_reference, options = {})
|
||||
money = amount(money)
|
||||
case normalize(credit_card_or_reference)
|
||||
when /1$/
|
||||
Response.new(true, SUCCESS_MESSAGE, {:authorized_amount => money}, :test => true, :authorization => AUTHORIZATION )
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:authorized_amount => money, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
raise Error, ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def purchase(money, credit_card_or_reference, options = {})
|
||||
money = amount(money)
|
||||
case normalize(credit_card_or_reference)
|
||||
when /1$/, AUTHORIZATION
|
||||
Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true, :authorization => AUTHORIZATION)
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true)
|
||||
else
|
||||
raise Error, ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def recurring(money, credit_card_or_reference, options = {})
|
||||
money = amount(money)
|
||||
case normalize(credit_card_or_reference)
|
||||
when /1$/
|
||||
Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true)
|
||||
else
|
||||
raise Error, ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def credit(money, credit_card_or_reference, options = {})
|
||||
if credit_card_or_reference.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
return refund(money, credit_card_or_reference, options)
|
||||
end
|
||||
|
||||
money = amount(money)
|
||||
case normalize(credit_card_or_reference)
|
||||
when /1$/
|
||||
Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true )
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
raise Error, CREDIT_ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
money = amount(money)
|
||||
case reference
|
||||
when /1$/
|
||||
raise Error, REFUND_ERROR_MESSAGE
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
|
||||
end
|
||||
end
|
||||
|
||||
def capture(money, reference, options = {})
|
||||
money = amount(money)
|
||||
case reference
|
||||
when /1$/
|
||||
raise Error, CAPTURE_ERROR_MESSAGE
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
|
||||
end
|
||||
end
|
||||
|
||||
def void(reference, options = {})
|
||||
case reference
|
||||
when /1$/
|
||||
raise Error, VOID_ERROR_MESSAGE
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:authorization => reference, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
Response.new(true, SUCCESS_MESSAGE, {:authorization => reference}, :test => true)
|
||||
end
|
||||
end
|
||||
|
||||
def store(credit_card_or_reference, options = {})
|
||||
case normalize(credit_card_or_reference)
|
||||
when /1$/
|
||||
Response.new(true, SUCCESS_MESSAGE, {:billingid => '1'}, :test => true, :authorization => AUTHORIZATION)
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:billingid => nil, :error => FAILURE_MESSAGE }, :test => true)
|
||||
else
|
||||
raise Error, ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
def unstore(reference, options = {})
|
||||
case reference
|
||||
when /1$/
|
||||
Response.new(true, SUCCESS_MESSAGE, {}, :test => true)
|
||||
when /2$/
|
||||
Response.new(false, FAILURE_MESSAGE, {:error => FAILURE_MESSAGE },:test => true)
|
||||
else
|
||||
raise Error, UNSTORE_ERROR_MESSAGE
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize(credit_card_or_reference)
|
||||
if credit_card_or_reference.respond_to?(:number)
|
||||
credit_card_or_reference.number
|
||||
else
|
||||
credit_card_or_reference.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
19
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree.rb
vendored
Normal file
19
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree.rb
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
require File.dirname(__FILE__) + '/braintree/braintree_common'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BraintreeGateway < Gateway
|
||||
include BraintreeCommon
|
||||
|
||||
self.abstract_class = true
|
||||
|
||||
def self.new(options={})
|
||||
if options.has_key?(:login)
|
||||
BraintreeOrangeGateway.new(options)
|
||||
else
|
||||
BraintreeBlueGateway.new(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module BraintreeCommon
|
||||
def self.included(base)
|
||||
base.supported_countries = ['US']
|
||||
base.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
|
||||
base.homepage_url = 'http://www.braintreepaymentsolutions.com'
|
||||
base.display_name = 'Braintree'
|
||||
base.default_currency = 'USD'
|
||||
end
|
||||
end
|
||||
401
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree_blue.rb
vendored
Normal file
401
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree_blue.rb
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
require File.dirname(__FILE__) + '/braintree/braintree_common'
|
||||
|
||||
begin
|
||||
require "braintree"
|
||||
rescue LoadError
|
||||
raise "Could not load the braintree gem. Use `gem install braintree` to install it."
|
||||
end
|
||||
|
||||
raise "Need braintree gem 2.x.y. Run `gem install braintree --version '~>2.0'` to get the correct version." unless Braintree::Version::Major == 2
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# For more information on the Braintree Gateway please visit their
|
||||
# {Developer Portal}[https://www.braintreepayments.com/developers]
|
||||
#
|
||||
# ==== About this implementation
|
||||
#
|
||||
# This implementation leverages the Braintree-authored ruby gem:
|
||||
# https://github.com/braintree/braintree_ruby
|
||||
#
|
||||
# ==== Debugging Information
|
||||
#
|
||||
# Setting an ActiveMerchant +wiredump_device+ will automatically
|
||||
# configure the Braintree logger (via the Braintree gem's
|
||||
# configuration) when the BraintreeBlueGateway is instantiated.
|
||||
# Additionally, the log level will be set to +DEBUG+. Therefore,
|
||||
# all you have to do is set the +wiredump_device+ and you'll
|
||||
# get your debug output from your HTTP interactions with the
|
||||
# remote gateway. (Don't enable this in production.)
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# ActiveMerchant::Billing::BraintreeBlueGateway.wiredump_device = Logger.new(STDOUT)
|
||||
# # => #<Logger:0x107d385f8 ...>
|
||||
#
|
||||
# Braintree::Configuration.logger
|
||||
# # => (some other logger, created by default by the gem)
|
||||
#
|
||||
# Braintree::Configuration.logger.level
|
||||
# # => 1 (INFO)
|
||||
#
|
||||
# ActiveMerchant::Billing::BraintreeBlueGateway.new(:merchant_id => 'x', :public_key => 'x', :private_key => 'x')
|
||||
#
|
||||
# Braintree::Configuration.logger
|
||||
# # => #<Logger:0x107d385f8 ...>
|
||||
#
|
||||
# Braintree::Configuration.logger.level
|
||||
# # => 0 (DEBUG)
|
||||
#
|
||||
# Alternatively, you can avoid setting the +wiredump_device+
|
||||
# and set +Braintree::Configuration.logger+ and/or
|
||||
# +Braintree::Configuration.logger.level+ directly.
|
||||
class BraintreeBlueGateway < Gateway
|
||||
include BraintreeCommon
|
||||
|
||||
self.display_name = 'Braintree (Blue Platform)'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :merchant_id, :public_key, :private_key)
|
||||
@merchant_account_id = options[:merchant_account_id]
|
||||
|
||||
super
|
||||
|
||||
Braintree::Configuration.merchant_id = options[:merchant_id]
|
||||
Braintree::Configuration.public_key = options[:public_key]
|
||||
Braintree::Configuration.private_key = options[:private_key]
|
||||
Braintree::Configuration.environment = (options[:environment] || (test? ? :sandbox : :production)).to_sym
|
||||
Braintree::Configuration.custom_user_agent = "ActiveMerchant #{ActiveMerchant::VERSION}"
|
||||
|
||||
if wiredump_device
|
||||
Braintree::Configuration.logger = ((Logger === wiredump_device) ? wiredump_device : Logger.new(wiredump_device))
|
||||
Braintree::Configuration.logger.level = Logger::DEBUG
|
||||
else
|
||||
Braintree::Configuration.logger.level = Logger::WARN
|
||||
end
|
||||
end
|
||||
|
||||
def authorize(money, credit_card_or_vault_id, options = {})
|
||||
create_transaction(:sale, money, credit_card_or_vault_id, options)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
commit do
|
||||
result = Braintree::Transaction.submit_for_settlement(authorization, amount(money).to_s)
|
||||
Response.new(result.success?, message_from_result(result))
|
||||
end
|
||||
end
|
||||
|
||||
def purchase(money, credit_card_or_vault_id, options = {})
|
||||
authorize(money, credit_card_or_vault_id, options.merge(:submit_for_settlement => true))
|
||||
end
|
||||
|
||||
def credit(money, credit_card_or_vault_id, options = {})
|
||||
create_transaction(:credit, money, credit_card_or_vault_id, options)
|
||||
end
|
||||
|
||||
def refund(*args)
|
||||
# legacy signature: #refund(transaction_id, options = {})
|
||||
# new signature: #refund(money, transaction_id, options = {})
|
||||
money, transaction_id, _ = extract_refund_args(args)
|
||||
money = amount(money).to_s if money
|
||||
|
||||
commit do
|
||||
result = Braintree::Transaction.refund(transaction_id, money)
|
||||
Response.new(result.success?, message_from_result(result),
|
||||
{:braintree_transaction => (transaction_hash(result.transaction) if result.success?)},
|
||||
{:authorization => (result.transaction.id if result.success?)}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
commit do
|
||||
result = Braintree::Transaction.void(authorization)
|
||||
Response.new(result.success?, message_from_result(result),
|
||||
{:braintree_transaction => (transaction_hash(result.transaction) if result.success?)},
|
||||
{:authorization => (result.transaction.id if result.success?)}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def store(creditcard, options = {})
|
||||
commit do
|
||||
parameters = {
|
||||
:first_name => creditcard.first_name,
|
||||
:last_name => creditcard.last_name,
|
||||
:email => options[:email],
|
||||
:credit_card => {
|
||||
:number => creditcard.number,
|
||||
:cvv => creditcard.verification_value,
|
||||
:expiration_month => creditcard.month.to_s.rjust(2, "0"),
|
||||
:expiration_year => creditcard.year.to_s
|
||||
}
|
||||
}
|
||||
result = Braintree::Customer.create(merge_credit_card_options(parameters, options))
|
||||
Response.new(result.success?, message_from_result(result),
|
||||
{
|
||||
:braintree_customer => (customer_hash(result.customer) if result.success?),
|
||||
:customer_vault_id => (result.customer.id if result.success?)
|
||||
},
|
||||
:authorization => (result.customer.id if result.success?)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def update(vault_id, creditcard, options = {})
|
||||
braintree_credit_card = nil
|
||||
commit do
|
||||
braintree_credit_card = Braintree::Customer.find(vault_id).credit_cards.detect { |cc| cc.default? }
|
||||
return Response.new(false, 'Braintree::NotFoundError') if braintree_credit_card.nil?
|
||||
|
||||
options.merge!(:update_existing_token => braintree_credit_card.token)
|
||||
credit_card_params = merge_credit_card_options({
|
||||
:credit_card => {
|
||||
:number => creditcard.number,
|
||||
:cvv => creditcard.verification_value,
|
||||
:expiration_month => creditcard.month.to_s.rjust(2, "0"),
|
||||
:expiration_year => creditcard.year.to_s
|
||||
}
|
||||
}, options)[:credit_card]
|
||||
|
||||
result = Braintree::Customer.update(vault_id,
|
||||
:first_name => creditcard.first_name,
|
||||
:last_name => creditcard.last_name,
|
||||
:email => options[:email],
|
||||
:credit_card => credit_card_params
|
||||
)
|
||||
Response.new(result.success?, message_from_result(result),
|
||||
:braintree_customer => (customer_hash(Braintree::Customer.find(vault_id)) if result.success?),
|
||||
:customer_vault_id => (result.customer.id if result.success?)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def unstore(customer_vault_id)
|
||||
commit do
|
||||
Braintree::Customer.delete(customer_vault_id)
|
||||
Response.new(true, "OK")
|
||||
end
|
||||
end
|
||||
alias_method :delete, :unstore
|
||||
|
||||
private
|
||||
|
||||
def merge_credit_card_options(parameters, options)
|
||||
valid_options = {}
|
||||
options.each do |key, value|
|
||||
valid_options[key] = value if [:update_existing_token, :verify_card, :verification_merchant_account_id].include?(key)
|
||||
end
|
||||
|
||||
parameters[:credit_card] ||= {}
|
||||
parameters[:credit_card].merge!(:options => valid_options)
|
||||
parameters[:credit_card][:billing_address] = map_address(options[:billing_address]) if options[:billing_address]
|
||||
parameters
|
||||
end
|
||||
|
||||
def map_address(address)
|
||||
return {} if address.nil?
|
||||
mapped = {
|
||||
:street_address => address[:address1],
|
||||
:extended_address => address[:address2],
|
||||
:company => address[:company],
|
||||
:locality => address[:city],
|
||||
:region => address[:state],
|
||||
:postal_code => address[:zip],
|
||||
}
|
||||
if(address[:country] || address[:country_code_alpha2])
|
||||
mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2])
|
||||
elsif address[:country_name]
|
||||
mapped[:country_name] = address[:country_name]
|
||||
elsif address[:country_code_alpha3]
|
||||
mapped[:country_code_alpha3] = address[:country_code_alpha3]
|
||||
elsif address[:country_code_numeric]
|
||||
mapped[:country_code_numeric] = address[:country_code_numeric]
|
||||
end
|
||||
mapped
|
||||
end
|
||||
|
||||
def commit(&block)
|
||||
yield
|
||||
rescue Braintree::BraintreeError => ex
|
||||
Response.new(false, ex.class.to_s)
|
||||
end
|
||||
|
||||
def message_from_result(result)
|
||||
if result.success?
|
||||
"OK"
|
||||
elsif result.errors.size == 0 && result.credit_card_verification
|
||||
"Processor declined: #{result.credit_card_verification.processor_response_text} (#{result.credit_card_verification.processor_response_code})"
|
||||
else
|
||||
result.errors.map { |e| "#{e.message} (#{e.code})" }.join(" ")
|
||||
end
|
||||
end
|
||||
|
||||
def create_transaction(transaction_type, money, credit_card_or_vault_id, options)
|
||||
transaction_params = create_transaction_parameters(money, credit_card_or_vault_id, options)
|
||||
|
||||
commit do
|
||||
result = Braintree::Transaction.send(transaction_type, transaction_params)
|
||||
response_params, response_options, avs_result, cvv_result = {}, {}, {}, {}
|
||||
if result.success?
|
||||
response_params[:braintree_transaction] = transaction_hash(result.transaction)
|
||||
response_params[:customer_vault_id] = result.transaction.customer_details.id
|
||||
response_options[:authorization] = result.transaction.id
|
||||
end
|
||||
if result.transaction
|
||||
response_options[:avs_result] = {
|
||||
:code => nil, :message => nil,
|
||||
:street_match => result.transaction.avs_street_address_response_code,
|
||||
:postal_match => result.transaction.avs_postal_code_response_code
|
||||
}
|
||||
response_options[:cvv_result] = result.transaction.cvv_response_code
|
||||
if result.transaction.status == "gateway_rejected"
|
||||
message = "Transaction declined - gateway rejected"
|
||||
else
|
||||
message = "#{result.transaction.processor_response_code} #{result.transaction.processor_response_text}"
|
||||
end
|
||||
else
|
||||
message = message_from_result(result)
|
||||
end
|
||||
response = Response.new(result.success?, message, response_params, response_options)
|
||||
response.cvv_result['message'] = ''
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
def extract_refund_args(args)
|
||||
options = args.extract_options!
|
||||
|
||||
# money, transaction_id, options
|
||||
if args.length == 1 # legacy signature
|
||||
return nil, args[0], options
|
||||
elsif args.length == 2
|
||||
return args[0], args[1], options
|
||||
else
|
||||
raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
|
||||
end
|
||||
end
|
||||
|
||||
def customer_hash(customer)
|
||||
credit_cards = customer.credit_cards.map do |cc|
|
||||
{
|
||||
"bin" => cc.bin,
|
||||
"expiration_date" => cc.expiration_date,
|
||||
"token" => cc.token,
|
||||
"last_4" => cc.last_4,
|
||||
"card_type" => cc.card_type,
|
||||
"masked_number" => cc.masked_number
|
||||
}
|
||||
end
|
||||
|
||||
{
|
||||
"email" => customer.email,
|
||||
"first_name" => customer.first_name,
|
||||
"last_name" => customer.last_name,
|
||||
"credit_cards" => credit_cards,
|
||||
"id" => customer.id
|
||||
}
|
||||
end
|
||||
|
||||
def transaction_hash(transaction)
|
||||
if transaction.vault_customer
|
||||
vault_customer = {
|
||||
}
|
||||
vault_customer["credit_cards"] = transaction.vault_customer.credit_cards.map do |cc|
|
||||
{
|
||||
"bin" => cc.bin
|
||||
}
|
||||
end
|
||||
else
|
||||
vault_customer = nil
|
||||
end
|
||||
|
||||
customer_details = {
|
||||
"id" => transaction.customer_details.id,
|
||||
"email" => transaction.customer_details.email
|
||||
}
|
||||
|
||||
billing_details = {
|
||||
"street_address" => transaction.billing_details.street_address,
|
||||
"extended_address" => transaction.billing_details.extended_address,
|
||||
"company" => transaction.billing_details.company,
|
||||
"locality" => transaction.billing_details.locality,
|
||||
"region" => transaction.billing_details.region,
|
||||
"postal_code" => transaction.billing_details.postal_code,
|
||||
"country_name" => transaction.billing_details.country_name,
|
||||
}
|
||||
|
||||
shipping_details = {
|
||||
"street_address" => transaction.shipping_details.street_address,
|
||||
"extended_address" => transaction.shipping_details.extended_address,
|
||||
"company" => transaction.shipping_details.company,
|
||||
"locality" => transaction.shipping_details.locality,
|
||||
"region" => transaction.shipping_details.region,
|
||||
"postal_code" => transaction.shipping_details.postal_code,
|
||||
"country_name" => transaction.shipping_details.country_name,
|
||||
}
|
||||
credit_card_details = {
|
||||
"masked_number" => transaction.credit_card_details.masked_number,
|
||||
"bin" => transaction.credit_card_details.bin,
|
||||
"last_4" => transaction.credit_card_details.last_4,
|
||||
"card_type" => transaction.credit_card_details.card_type,
|
||||
"token" => transaction.credit_card_details.token
|
||||
}
|
||||
|
||||
{
|
||||
"order_id" => transaction.order_id,
|
||||
"status" => transaction.status,
|
||||
"credit_card_details" => credit_card_details,
|
||||
"customer_details" => customer_details,
|
||||
"billing_details" => billing_details,
|
||||
"shipping_details" => shipping_details,
|
||||
"vault_customer" => vault_customer,
|
||||
"merchant_account_id" => transaction.merchant_account_id
|
||||
}
|
||||
end
|
||||
|
||||
def create_transaction_parameters(money, credit_card_or_vault_id, options)
|
||||
parameters = {
|
||||
:amount => amount(money).to_s,
|
||||
:order_id => options[:order_id],
|
||||
:customer => {
|
||||
:id => options[:store] == true ? "" : options[:store],
|
||||
:email => options[:email]
|
||||
},
|
||||
:options => {
|
||||
:store_in_vault => options[:store] ? true : false,
|
||||
:submit_for_settlement => options[:submit_for_settlement]
|
||||
}
|
||||
}
|
||||
|
||||
if merchant_account_id = (options[:merchant_account_id] || @merchant_account_id)
|
||||
parameters[:merchant_account_id] = merchant_account_id
|
||||
end
|
||||
|
||||
if options[:recurring]
|
||||
parameters[:recurring] = true
|
||||
end
|
||||
|
||||
if credit_card_or_vault_id.is_a?(String) || credit_card_or_vault_id.is_a?(Integer)
|
||||
parameters[:customer_id] = credit_card_or_vault_id
|
||||
else
|
||||
parameters[:customer].merge!(
|
||||
:first_name => credit_card_or_vault_id.first_name,
|
||||
:last_name => credit_card_or_vault_id.last_name
|
||||
)
|
||||
parameters[:credit_card] = {
|
||||
:number => credit_card_or_vault_id.number,
|
||||
:cvv => credit_card_or_vault_id.verification_value,
|
||||
:expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
|
||||
:expiration_year => credit_card_or_vault_id.year.to_s
|
||||
}
|
||||
end
|
||||
parameters[:billing] = map_address(options[:billing_address]) if options[:billing_address]
|
||||
parameters[:shipping] = map_address(options[:shipping_address]) if options[:shipping_address]
|
||||
parameters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
19
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree_orange.rb
vendored
Normal file
19
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/braintree_orange.rb
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
require File.dirname(__FILE__) + '/smart_ps.rb'
|
||||
require File.dirname(__FILE__) + '/braintree/braintree_common'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class BraintreeOrangeGateway < SmartPs
|
||||
include BraintreeCommon
|
||||
|
||||
self.display_name = 'Braintree (Orange Platform)'
|
||||
|
||||
self.live_url = self.test_url = 'https://secure.braintreepaymentgateway.com/api/transact.php'
|
||||
|
||||
def add_processor(post, options)
|
||||
post[:processor_id] = options[:processor] unless options[:processor].nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
23
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_save.rb
vendored
Normal file
23
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_save.rb
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class CardSaveGateway < IridiumGateway
|
||||
#CardSave lets you handle failovers on payments by providing 3 gateways in case one happens to be down
|
||||
#URLS = ['https://gw1.cardsaveonlinepayments.com:4430/','https://gw2.cardsaveonlinepayments.com:4430/','https://gw3.cardsaveonlinepayments.com:4430/']
|
||||
|
||||
self.money_format = :cents
|
||||
self.default_currency = 'GBP'
|
||||
self.supported_cardtypes = [ :visa, :switch, :maestro, :master, :solo, :american_express, :jcb ]
|
||||
self.supported_countries = [ 'GB' ]
|
||||
self.homepage_url = 'http://www.cardsave.net/'
|
||||
self.display_name = 'CardSave'
|
||||
|
||||
def initialize(options={})
|
||||
super
|
||||
@test_url = 'https://gw1.cardsaveonlinepayments.com:4430/'
|
||||
@live_url = 'https://gw1.cardsaveonlinepayments.com:4430/'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
225
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_stream.rb
vendored
Normal file
225
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_stream.rb
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
#
|
||||
# CardStream supports the following credit cards, which are auto-detected by
|
||||
# the gateway based on the card number used:
|
||||
# * AM American Express
|
||||
# * Diners Club
|
||||
# * Electron
|
||||
# * JCB
|
||||
# * UK Maestro
|
||||
# * Maestro International
|
||||
# * Mastercard
|
||||
# * Solo
|
||||
# * Style
|
||||
# * Switch
|
||||
# * Visa Credit
|
||||
# * Visa Debit
|
||||
# * Visa Purchasing
|
||||
#
|
||||
class CardStreamGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://gateway.cardstream.com/process.ashx'
|
||||
|
||||
self.money_format = :cents
|
||||
self.default_currency = 'GBP'
|
||||
self.supported_countries = ['GB']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :solo, :switch]
|
||||
self.homepage_url = 'http://www.cardstream.com/'
|
||||
self.display_name = 'CardStream'
|
||||
|
||||
APPROVED = '00'
|
||||
|
||||
CURRENCY_CODES = {
|
||||
"AUD"=> '036',
|
||||
"CAD"=> '124',
|
||||
"CZK"=> '203',
|
||||
"DKK"=> '208',
|
||||
"HKD"=> '344',
|
||||
"ICK"=> '352',
|
||||
"JPY"=> '392',
|
||||
"NOK"=> '578',
|
||||
"SGD"=> '702',
|
||||
"SEK"=> '752',
|
||||
"CHF"=> '756',
|
||||
"GBP"=> '826',
|
||||
"USD"=> '840',
|
||||
"EUR"=> '978'
|
||||
}
|
||||
|
||||
TRANSACTIONS = {
|
||||
:purchase => 'ESALE_KEYED',
|
||||
:refund => 'EREFUND_KEYED',
|
||||
:authorization => 'ESALE_KEYED'
|
||||
}
|
||||
|
||||
CVV_CODE = {
|
||||
'0' => 'U',
|
||||
'1' => 'P',
|
||||
'2' => 'M',
|
||||
'4' => 'N'
|
||||
}
|
||||
|
||||
# 0 - No additional information available.
|
||||
# 1 - Postcode not checked.
|
||||
# 2 - Postcode matched.
|
||||
# 4 - Postcode not matched.
|
||||
# 8 - Postcode partially matched.
|
||||
AVS_POSTAL_MATCH = {
|
||||
"0" => nil,
|
||||
"1" => nil,
|
||||
"2" => "Y",
|
||||
"4" => "N",
|
||||
"8" => "N"
|
||||
}
|
||||
|
||||
# 0 - No additional information available.
|
||||
# 1 - Address numeric not checked.
|
||||
# 2 - Address numeric matched.
|
||||
# 4 - Address numeric not matched.
|
||||
# 8 - Address numeric partially matched.
|
||||
AVS_STREET_MATCH = {
|
||||
"0" => nil,
|
||||
"1" => nil,
|
||||
"2" => "Y",
|
||||
"4" => "N",
|
||||
"8" => "N"
|
||||
}
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
post = {}
|
||||
|
||||
add_amount(post, money, options)
|
||||
add_invoice(post, money, credit_card, options)
|
||||
add_credit_card(post, credit_card)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit(:purchase, post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_amount(post, money, options)
|
||||
add_pair(post, :Amount, amount(money), :required => true)
|
||||
add_pair(post, :CurrencyCode, currency_code(options[:currency] || currency(money)), :required => true)
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
add_pair(post, :BillingEmail, options[:email])
|
||||
add_pair(post, :BillingPhoneNumber, options[:phone])
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
address = options[:billing_address] || options[:address]
|
||||
|
||||
return if address.nil?
|
||||
|
||||
add_pair(post, :BillingStreet, address[:address1])
|
||||
add_pair(post, :BillingHouseNumber, address[:address2])
|
||||
add_pair(post, :BillingCity, address[:city])
|
||||
add_pair(post, :BillingState, address[:state])
|
||||
add_pair(post, :BillingPostCode, address[:zip])
|
||||
end
|
||||
|
||||
def add_invoice(post, money, credit_card, options)
|
||||
add_pair(post, :TransactionUnique, options[:order_id], :required => true)
|
||||
add_pair(post, :OrderDesc, options[:description] || options[:order_id], :required => true)
|
||||
|
||||
if [ 'american_express', 'diners_club' ].include?(card_brand(credit_card).to_s)
|
||||
add_pair(post, :AEIT1Quantity, 1)
|
||||
add_pair(post, :AEIT1Description, (options[:description] || options[:order_id]).slice(0, 15))
|
||||
add_pair(post, :AEIT1GrossValue, amount(money))
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_card(post, credit_card)
|
||||
add_pair(post, :CardName, credit_card.name, :required => true)
|
||||
add_pair(post, :CardNumber, credit_card.number, :required => true)
|
||||
|
||||
add_pair(post, :ExpiryDateMM, format(credit_card.month, :two_digits), :required => true)
|
||||
add_pair(post, :ExpiryDateYY, format(credit_card.year, :two_digits), :required => true)
|
||||
|
||||
if requires_start_date_or_issue_number?(credit_card)
|
||||
add_pair(post, :StartDateMM, format(credit_card.start_month, :two_digits))
|
||||
add_pair(post, :StartDateYY, format(credit_card.start_year, :two_digits))
|
||||
|
||||
add_pair(post, :IssueNumber, credit_card.issue_number)
|
||||
end
|
||||
|
||||
add_pair(post, :CV2, credit_card.verification_value)
|
||||
end
|
||||
|
||||
def commit(action, parameters)
|
||||
response = parse( ssl_post(self.live_url, post_data(action, parameters)) )
|
||||
|
||||
Response.new(response[:response_code] == APPROVED, message_from(response), response,
|
||||
:test => test?,
|
||||
:authorization => response[:cross_reference],
|
||||
:cvv_result => CVV_CODE[ response[:avscv2_response_code].to_s[0, 1] ],
|
||||
:avs_result => {
|
||||
:street_match => AVS_STREET_MATCH[ response[:avscv2_response_code].to_s[2, 1] ],
|
||||
:postal_match => AVS_POSTAL_MATCH[ response[:avscv2_response_code].to_s[1, 1] ]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def message_from(results)
|
||||
results[:response_code] == APPROVED ? "APPROVED" : results[:message]
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
parameters.update(
|
||||
:MerchantPassword => @options[:password],
|
||||
:MerchantID => @options[:login],
|
||||
:MessageType => TRANSACTIONS[action],
|
||||
:CallBack => "disable",
|
||||
:DuplicateDelay => "0",
|
||||
:EchoCardType => "YES",
|
||||
:EchoAmount => "YES",
|
||||
:EchoAVSCV2ResponseCode => "YES",
|
||||
:ReturnAVSCV2Message => "YES",
|
||||
:CountryCode => '826' # 826 for UK based merchant
|
||||
)
|
||||
|
||||
add_pair(parameters, :Dispatch, action == :authorization ? "LATER" : "NOW")
|
||||
|
||||
parameters.collect { |key, value| "VP#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
# VPCrossReference
|
||||
# The value in VPCrossReference on a success transaction will contain
|
||||
# a unique reference that you may use to run future transactions.
|
||||
# Please note that cross reference transactions must come a static IP
|
||||
# addressed that has been pre-registered with Cardstream. To
|
||||
# register an IP address please send it to support@cardstream.com
|
||||
# with your Cardstream issued merchant ID and it will be added to
|
||||
# your account.
|
||||
def parse(body)
|
||||
result = {}
|
||||
pairs = body.split("&")
|
||||
pairs.each do |pair|
|
||||
a = pair.split("=")
|
||||
result[a[0].gsub(/^VP/,'').underscore.to_sym] = a[1]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def currency_code(currency)
|
||||
CURRENCY_CODES[currency]
|
||||
end
|
||||
|
||||
def add_pair(post, key, value, options = {})
|
||||
post[key] = value if !value.blank? || options[:required]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
155
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_stream_modern.rb
vendored
Normal file
155
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/card_stream_modern.rb
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class CardStreamModernGateway < Gateway
|
||||
self.test_url = self.live_url = 'https://gateway.cardstream.com/direct/'
|
||||
self.money_format = :cents
|
||||
self.default_currency = 'GBP'
|
||||
self.supported_countries = ['GB']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :solo, :switch]
|
||||
self.homepage_url = 'http://www.cardstream.com/'
|
||||
self.display_name = 'CardStream'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
if(options[:threeDSRequired])
|
||||
@threeDSRequired = options[:threeDSRequired]
|
||||
else
|
||||
@threeDSRequired = 'N'
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_invoice(post, creditcard, money, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, creditcard, options)
|
||||
add_customer_data(post, options)
|
||||
commit('PREAUTH', post)
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_invoice(post, creditcard, money, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, creditcard, options)
|
||||
add_customer_data(post, options)
|
||||
commit('SALE', post)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
post = {}
|
||||
add_pair(post, :xref, authorization)
|
||||
add_amount(post, money, options)
|
||||
commit('SALE', post)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
post = {}
|
||||
add_pair(post, :xref, authorization)
|
||||
add_amount(post, money, options)
|
||||
commit('REFUND', post)
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
post = {}
|
||||
add_pair(post, :xref, authorization)
|
||||
commit('REFUND', post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_amount(post, money, options)
|
||||
add_pair(post, :amount, amount(money), :required => true)
|
||||
add_pair(post, :currencyCode, options[:currency] || self.default_currency)
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
address = options[:billing_address] || options[:address]
|
||||
add_pair(post, :customerPostCode, address[:zip])
|
||||
add_pair(post, :customerEmail, options[:email])
|
||||
add_pair(post, :customerPhone, options[:phone])
|
||||
end
|
||||
|
||||
def add_address(post, creditcard, options)
|
||||
address = options[:billing_address] || options[:address]
|
||||
|
||||
return if address.nil?
|
||||
|
||||
add_pair(post, :customerAddress, address[:address1] + " " + (address[:address2].nil? ? "" : address[:address2]) )
|
||||
add_pair(post, :customerPostCode, address[:zip])
|
||||
end
|
||||
|
||||
def add_invoice(post, credit_card, money, options)
|
||||
add_pair(post, :transactionUnique, options[:order_id], :required => true)
|
||||
add_pair(post, :orderRef, options[:description] || options[:order_id], :required => true)
|
||||
if [ 'american_express', 'diners_club' ].include?(card_brand(credit_card).to_s)
|
||||
add_pair(post, :item1Quantity, 1)
|
||||
add_pair(post, :item1Description, (options[:description] || options[:order_id]).slice(0, 15))
|
||||
add_pair(post, :item1GrossValue, amount(money))
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(post, credit_card)
|
||||
add_pair(post, :customerName, credit_card.name, :required => true)
|
||||
add_pair(post, :cardNumber, credit_card.number, :required => true)
|
||||
|
||||
add_pair(post, :cardExpiryMonth, format(credit_card.month, :two_digits), :required => true)
|
||||
add_pair(post, :cardExpiryYear, format(credit_card.year, :two_digits), :required => true)
|
||||
|
||||
if requires_start_date_or_issue_number?(credit_card)
|
||||
add_pair(post, :cardStartMonth, format(credit_card.start_month, :two_digits))
|
||||
add_pair(post, :cardStartYear, format(credit_card.start_year, :two_digits))
|
||||
|
||||
add_pair(post, :cardIssueNumber, credit_card.issue_number)
|
||||
end
|
||||
|
||||
add_pair(post, :cardCVV, credit_card.verification_value)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
result = {}
|
||||
pairs = body.split("&")
|
||||
pairs.each do |pair|
|
||||
a = pair.split("=")
|
||||
result[a[0].to_sym] = CGI.unescape(a[1])
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def commit(action, parameters)
|
||||
response = parse( ssl_post(self.live_url, post_data(action, parameters)) )
|
||||
|
||||
Response.new(response[:responseCode] == "0",
|
||||
response[:responseCode] == "0" ? "APPROVED" : response[:responseMessage],
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => response[:xref],
|
||||
:avs_result => {
|
||||
:street_match => response[:addressCheck],
|
||||
:postal_match => response[:postcodeCheck],
|
||||
},
|
||||
:cvv_result => response[:cv2Check]
|
||||
)
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
parameters.update(
|
||||
:merchantID => @options[:login],
|
||||
:action => action,
|
||||
:type => '1', #Ecommerce
|
||||
:countryCode => self.supported_countries[0],
|
||||
:threeDSRequired => @threeDSRequired #Disable 3d secure by default
|
||||
)
|
||||
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
def add_pair(post, key, value, options = {})
|
||||
post[key] = value if !value.blank? || options[:required]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
156
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/cc5.rb
vendored
Normal file
156
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/cc5.rb
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# CC5 API is used by many banks in Turkey. Extend this base class to provide
|
||||
# concrete implementations.
|
||||
class CC5Gateway < Gateway
|
||||
self.default_currency = 'TRY'
|
||||
|
||||
CURRENCY_CODES = {
|
||||
'TRY' => 949,
|
||||
'YTL' => 949,
|
||||
'TRL' => 949,
|
||||
'TL' => 949,
|
||||
'USD' => 840,
|
||||
'EUR' => 978,
|
||||
'GBP' => 826,
|
||||
'JPY' => 392
|
||||
}
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :client_id)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
commit(build_sale_request('Auth', money, creditcard, options))
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
commit(build_sale_request('PreAuth', money, creditcard, options))
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
commit(build_capture_request(money, authorization, options))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def build_sale_request(type, money, creditcard, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
|
||||
xml.tag! 'CC5Request' do
|
||||
add_login_tags(xml)
|
||||
xml.tag! 'OrderId', options[:order_id]
|
||||
xml.tag! 'Type', type
|
||||
xml.tag! 'Number', creditcard.number
|
||||
xml.tag! 'Expires', [format(creditcard.month, :two_digits), format(creditcard.year, :two_digits)].join('/')
|
||||
xml.tag! 'Cvv2Val', creditcard.verification_value
|
||||
add_amount_tags(money, options, xml)
|
||||
xml.tag! 'Email', options[:email] if options[:email]
|
||||
|
||||
if(address = (options[:billing_address] || options[:address]))
|
||||
xml.tag! 'BillTo' do
|
||||
add_address(xml, address)
|
||||
end
|
||||
xml.tag! 'ShipTo' do
|
||||
add_address(xml, address)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_capture_request(money, authorization, options = {})
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
|
||||
xml.tag! 'CC5Request' do
|
||||
add_login_tags(xml)
|
||||
xml.tag! 'OrderId', authorization
|
||||
xml.tag! 'Type', 'PostAuth'
|
||||
add_amount_tags(money, options, xml)
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(xml, address)
|
||||
xml.tag! 'Name', normalize(address[:name])
|
||||
xml.tag! 'Street1', normalize(address[:address1])
|
||||
xml.tag! 'Street2', normalize(address[:address2]) if address[:address2]
|
||||
xml.tag! 'City', normalize(address[:city])
|
||||
xml.tag! 'PostalCode', address[:zip]
|
||||
xml.tag! 'Country', normalize(address[:country])
|
||||
xml.tag! 'Company', normalize(address[:company])
|
||||
xml.tag! 'TelVoice', address[:phone].to_s.gsub(/[^0-9]/, '') if address[:phone]
|
||||
end
|
||||
|
||||
def add_login_tags(xml)
|
||||
xml.tag! 'Name', @options[:login]
|
||||
xml.tag! 'Password', @options[:password]
|
||||
xml.tag! 'ClientId', @options[:client_id]
|
||||
xml.tag! 'Mode', (test? ? 'T' : 'P')
|
||||
end
|
||||
|
||||
def add_amount_tags(money, options, xml)
|
||||
xml.tag! 'Total', amount(money)
|
||||
xml.tag! 'Currency', currency_code(options[:currency] || currency(money))
|
||||
end
|
||||
|
||||
def currency_code(currency)
|
||||
(CURRENCY_CODES[currency] || CURRENCY_CODES[default_currency])
|
||||
end
|
||||
|
||||
def commit(request)
|
||||
raw_response = ssl_post((test? ? self.test_url : self.live_url), "DATA=" + request)
|
||||
|
||||
response = parse(raw_response)
|
||||
|
||||
success = success?(response)
|
||||
|
||||
Response.new(
|
||||
success,
|
||||
(success ? 'Approved' : "Declined (Reason: #{response[:proc_return_code]} - #{response[:err_msg]})"),
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => response[:order_id]
|
||||
)
|
||||
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
|
||||
|
||||
def success?(response)
|
||||
(response[:response] == "Approved")
|
||||
end
|
||||
|
||||
def normalize(text)
|
||||
return unless text
|
||||
|
||||
if ActiveSupport::Inflector.method(:transliterate).arity == -2
|
||||
ActiveSupport::Inflector.transliterate(text,'')
|
||||
elsif RUBY_VERSION >= '1.9'
|
||||
text.gsub(/[^\x00-\x7F]+/, '')
|
||||
else
|
||||
ActiveSupport::Inflector.transliterate(text).to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
277
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/certo_direct.rb
vendored
Normal file
277
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/certo_direct.rb
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class CertoDirectGateway < Gateway
|
||||
self.live_url = self.test_url = "https://secure.certodirect.com/gateway/process/v2"
|
||||
|
||||
self.supported_countries = [
|
||||
"BE", "BG", "CZ", "DK", "DE", "EE", "IE", "EL", "ES", "FR",
|
||||
"IT", "CY", "LV", "LT", "LU", "HU", "MT", "NL", "AT", "PL",
|
||||
"PT", "RO", "SI", "SK", "FI", "SE", "GB"
|
||||
]
|
||||
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.homepage_url = 'http://www.certodirect.com/'
|
||||
self.display_name = 'CertoDirect'
|
||||
|
||||
# Creates a new CertoDirectGateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The CertoDirect Shop ID (REQUIRED)
|
||||
# * <tt>:password</tt> -- The CertoDirect Shop Password. (REQUIRED)
|
||||
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
||||
# Otherwise, perform transactions against the production server.
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> -- The CreditCard details for the transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
def purchase(money, credit_card, options = {})
|
||||
requires!(options, :email, :currency, :ip, :description)
|
||||
|
||||
commit(build_sale_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
# Refund a transaction.
|
||||
#
|
||||
# This transaction indicates to the gateway that
|
||||
# money should flow from the merchant to the customer.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
||||
# * <tt>identification</tt> -- The ID of the original order against which the refund is being issued.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
def refund(money, identification, options = {})
|
||||
requires!(options, :reason)
|
||||
|
||||
commit(build_refund_request(money, identification, options))
|
||||
end
|
||||
|
||||
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
||||
# charge the card.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> -- The CreditCard details for the transaction.
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
def authorize(money, credit_card, options = {})
|
||||
requires!(options, :email, :currency, :ip, :description)
|
||||
|
||||
commit(build_authorize_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
# Captures the funds from an authorized transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
||||
# * <tt>identification</tt> -- The authorization returned from the previous authorize request.
|
||||
def capture(money, identification, options = {})
|
||||
commit(build_capture_request(money, identification))
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
||||
# * <tt>identification</tt> - The authorization returned from the previous authorize request.
|
||||
def void(money, identification, options = {})
|
||||
commit(build_void_request(money, identification))
|
||||
end
|
||||
|
||||
# Create a recurring payment.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
def recurring(identification, options={})
|
||||
commit(build_recurring_request(identification, options))
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def commit(request_xml)
|
||||
begin
|
||||
response = Hash.from_xml(ssl_post(self.live_url, request_xml, headers))
|
||||
Response.new(success?(response),
|
||||
message(response),
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => authorization(response))
|
||||
rescue ResponseError => e
|
||||
raise e unless e.response.code == '403'
|
||||
response = Hash.from_xml(e.response.body)['response']
|
||||
Response.new(false, message(response), {}, :test => test?)
|
||||
end
|
||||
end
|
||||
|
||||
def build_sale_request(money, credit_card, options)
|
||||
build_request_xml('Sale') do |xml|
|
||||
add_order(xml, money, credit_card, options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_authorize_request(money, credit_card, options)
|
||||
build_request_xml('Authorize') do |xml|
|
||||
add_order(xml, money, credit_card, options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_refund_request(money, identification, options)
|
||||
build_request_xml('Refund') do |xml|
|
||||
add_reference_info(xml, money, identification, options)
|
||||
xml.tag! 'reason', options[:reason]
|
||||
end
|
||||
end
|
||||
|
||||
def build_capture_request(money, identification)
|
||||
build_request_xml('Capture') do |xml|
|
||||
add_reference_info(xml, money, identification, options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_void_request(money, identification)
|
||||
build_request_xml('Void') do |xml|
|
||||
add_reference_info(xml, money, identification, options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_recurring_request(identification, options)
|
||||
build_request_xml('Sale') do |xml|
|
||||
xml.tag! 'order' do |xml|
|
||||
xml.tag!('test', 'true') if test?
|
||||
xml.tag! 'initial_order_id', identification, :type => 'integer'
|
||||
|
||||
add_order_details(xml, options[:amount], options) if has_any_order_details_key?(options)
|
||||
add_address(xml, 'billing_address', options[:billing_address]) if options[:billing_address]
|
||||
add_address(xml, 'shipping_address', options[:shipping_address]) if options[:shipping_address]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_request_xml(type, &block)
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.tag! 'transaction' do
|
||||
xml.tag! 'type', type
|
||||
yield(xml)
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def add_order(xml, money, credit_card, options)
|
||||
xml.tag! 'order' do
|
||||
xml.tag!('test', 'true') if test?
|
||||
|
||||
xml.tag!('return_url', options[:return_url]) if options[:return_url]
|
||||
xml.tag!('cancel_url', options[:cancel_url]) if options[:cancel_url]
|
||||
|
||||
xml.tag! 'payment_method_type', 'CreditCard'
|
||||
xml.tag! 'payment_method' do
|
||||
xml.tag! 'number', credit_card.number
|
||||
xml.tag! 'exp_month', "%02i" % credit_card.month
|
||||
xml.tag! 'exp_year', credit_card.year
|
||||
xml.tag! 'holder', credit_card.name
|
||||
xml.tag! 'verification_value', credit_card.verification_value
|
||||
end
|
||||
|
||||
add_order_details(xml, money, options)
|
||||
add_address(xml, 'billing_address', options[:billing_address]) if options[:billing_address]
|
||||
add_address(xml, 'shipping_address', options[:shipping_address]) if options[:shipping_address]
|
||||
end
|
||||
end
|
||||
|
||||
def add_order_details(xml, money, options)
|
||||
xml.tag! 'details' do
|
||||
xml.tag!('amount', localized_amount(money, options[:currency]), :type => 'decimal') if money
|
||||
xml.tag!('currency', options[:currency]) if options[:currency]
|
||||
xml.tag!('email', options[:email]) if options[:email]
|
||||
xml.tag!('ip', options[:ip]) if options[:ip]
|
||||
xml.tag!('shipping', options[:shipping], :type => 'decimal') if options[:shipping]
|
||||
xml.tag!('description', options[:description]) if options[:description]
|
||||
end
|
||||
end
|
||||
|
||||
def add_reference_info(xml, money, identification, options)
|
||||
xml.tag! 'order_id', identification, :type => 'integer'
|
||||
xml.tag! 'amount', localized_amount(money, options[:currency]), :type => 'decimal'
|
||||
end
|
||||
|
||||
def add_address(xml, address_type, address)
|
||||
xml.tag! address_type do
|
||||
xml.tag! 'address', address[:address1]
|
||||
xml.tag! 'city', address[:city]
|
||||
xml.tag! 'country', address[:country]
|
||||
xml.tag! 'first_name', address[:first_name]
|
||||
xml.tag! 'last_name', address[:last_name]
|
||||
xml.tag! 'state', address[:state]
|
||||
xml.tag! 'phone', address[:phone]
|
||||
xml.tag! 'zip', address[:zip]
|
||||
end
|
||||
end
|
||||
|
||||
def has_any_order_details_key?(options)
|
||||
[ :currency, :amount, :email, :ip, :shipping, :description ].any? do |key|
|
||||
options.has_key?(key)
|
||||
end
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
%w(completed forwarding).include?(state(response)) and
|
||||
status(response) == 'success'
|
||||
end
|
||||
|
||||
def error?(response)
|
||||
response['errors']
|
||||
end
|
||||
|
||||
def state(response)
|
||||
response["transaction"].try(:[], "state")
|
||||
end
|
||||
|
||||
def status(response)
|
||||
response['transaction'].try(:[], 'response').try(:[], 'status')
|
||||
end
|
||||
|
||||
def authorization(response)
|
||||
error?(response) ? nil : response["transaction"]["order"]['id'].to_s
|
||||
end
|
||||
|
||||
def message(response)
|
||||
return response['errors'].join('; ') if error?(response)
|
||||
|
||||
if state(response) == 'completed'
|
||||
response["transaction"]["response"]["message"]
|
||||
else
|
||||
response['transaction']['message']
|
||||
end
|
||||
end
|
||||
|
||||
def headers
|
||||
{ 'authorization' => basic_auth,
|
||||
'Accept' => 'application/xml',
|
||||
'Content-Type' => 'application/xml' }
|
||||
end
|
||||
|
||||
def basic_auth
|
||||
'Basic ' + ["#{@options[:login]}:#{@options[:password]}"].pack('m').delete("\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
614
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/cyber_source.rb
vendored
Normal file
614
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/cyber_source.rb
vendored
Normal file
@@ -0,0 +1,614 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# See the remote and mocked unit test files for example usage. Pay special
|
||||
# attention to the contents of the options hash.
|
||||
#
|
||||
# Initial setup instructions can be found in
|
||||
# http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
|
||||
#
|
||||
# Debugging
|
||||
# If you experience an issue with this gateway be sure to examine the
|
||||
# transaction information from a general transaction search inside the
|
||||
# CyberSource Business Center for the full error messages including field
|
||||
# names.
|
||||
#
|
||||
# Important Notes
|
||||
# * For checks you can purchase and store.
|
||||
# * AVS and CVV only work against the production server. You will always
|
||||
# get back X for AVS and no response for CVV against the test server.
|
||||
# * Nexus is the list of states or provinces where you have a physical
|
||||
# presence. Nexus is used to calculate tax. Leave blank to tax everyone.
|
||||
# * If you want to calculate VAT for overseas customers you must supply a
|
||||
# registration number in the options hash as vat_reg_number.
|
||||
# * productCode is a value in the line_items hash that is used to tell
|
||||
# CyberSource what kind of item you are selling. It is used when
|
||||
# calculating tax/VAT.
|
||||
# * All transactions use dollar values.
|
||||
class CyberSourceGateway < Gateway
|
||||
self.test_url = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
|
||||
self.live_url = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
|
||||
|
||||
XSD_VERSION = "1.69"
|
||||
|
||||
# visa, master, american_express, discover
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.supported_countries = ['US']
|
||||
self.default_currency = 'USD'
|
||||
self.homepage_url = 'http://www.cybersource.com'
|
||||
self.display_name = 'CyberSource'
|
||||
|
||||
# map credit card to the CyberSource expected representation
|
||||
@@credit_card_codes = {
|
||||
:visa => '001',
|
||||
:master => '002',
|
||||
:american_express => '003',
|
||||
:discover => '004'
|
||||
}
|
||||
|
||||
# map response codes to something humans can read
|
||||
@@response_codes = {
|
||||
:r100 => "Successful transaction",
|
||||
:r101 => "Request is missing one or more required fields" ,
|
||||
:r102 => "One or more fields contains invalid data",
|
||||
:r150 => "General failure",
|
||||
:r151 => "The request was received but a server time-out occurred",
|
||||
:r152 => "The request was received, but a service timed out",
|
||||
:r200 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the AVS check",
|
||||
:r201 => "The issuing bank has questions about the request",
|
||||
:r202 => "Expired card",
|
||||
:r203 => "General decline of the card",
|
||||
:r204 => "Insufficient funds in the account",
|
||||
:r205 => "Stolen or lost card",
|
||||
:r207 => "Issuing bank unavailable",
|
||||
:r208 => "Inactive card or card not authorized for card-not-present transactions",
|
||||
:r209 => "American Express Card Identifiction Digits (CID) did not match",
|
||||
:r210 => "The card has reached the credit limit",
|
||||
:r211 => "Invalid card verification number",
|
||||
:r221 => "The customer matched an entry on the processor's negative file",
|
||||
:r230 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check",
|
||||
:r231 => "Invalid account number",
|
||||
:r232 => "The card type is not accepted by the payment processor",
|
||||
:r233 => "General decline by the processor",
|
||||
:r234 => "A problem exists with your CyberSource merchant configuration",
|
||||
:r235 => "The requested amount exceeds the originally authorized amount",
|
||||
:r236 => "Processor failure",
|
||||
:r237 => "The authorization has already been reversed",
|
||||
:r238 => "The authorization has already been captured",
|
||||
:r239 => "The requested transaction amount must match the previous transaction amount",
|
||||
:r240 => "The card type sent is invalid or does not correlate with the credit card number",
|
||||
:r241 => "The request ID is invalid",
|
||||
:r242 => "You requested a capture, but there is no corresponding, unused authorization record.",
|
||||
:r243 => "The transaction has already been settled or reversed",
|
||||
:r244 => "The bank account number failed the validation check",
|
||||
:r246 => "The capture or credit is not voidable because the capture or credit information has already been submitted to your processor",
|
||||
:r247 => "You requested a credit for a capture that was previously voided",
|
||||
:r250 => "The request was received, but a time-out occurred with the payment processor",
|
||||
:r254 => "Your CyberSource account is prohibited from processing stand-alone refunds",
|
||||
:r255 => "Your CyberSource account is not configured to process the service in the country you specified"
|
||||
}
|
||||
|
||||
# These are the options that can be used when creating a new CyberSource
|
||||
# Gateway object.
|
||||
#
|
||||
# :login => your username
|
||||
#
|
||||
# :password => the transaction key you generated in the Business Center
|
||||
#
|
||||
# :test => true sets the gateway to test mode
|
||||
#
|
||||
# :vat_reg_number => your VAT registration number
|
||||
#
|
||||
# :nexus => "WI CA QC" sets the states/provinces where you have a physical
|
||||
# presense for tax purposes
|
||||
#
|
||||
# :ignore_avs => true don't want to use AVS so continue processing even
|
||||
# if AVS would have failed
|
||||
#
|
||||
# :ignore_cvv => true don't want to use CVV so continue processing even
|
||||
# if CVV would have failed
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Request an authorization for an amount from CyberSource
|
||||
#
|
||||
# You must supply an :order_id in the options hash
|
||||
def authorize(money, creditcard_or_reference, options = {})
|
||||
requires!(options, :order_id)
|
||||
setup_address_hash(options)
|
||||
commit(build_auth_request(money, creditcard_or_reference, options), options )
|
||||
end
|
||||
|
||||
def auth_reversal(money, identification, options = {})
|
||||
commit(build_auth_reversal_request(money, identification, options), options)
|
||||
end
|
||||
|
||||
# Capture an authorization that has previously been requested
|
||||
def capture(money, authorization, options = {})
|
||||
setup_address_hash(options)
|
||||
commit(build_capture_request(money, authorization, options), options)
|
||||
end
|
||||
|
||||
# Purchase is an auth followed by a capture
|
||||
# You must supply an order_id in the options hash
|
||||
def purchase(money, payment_method_or_reference, options = {})
|
||||
requires!(options, :order_id)
|
||||
setup_address_hash(options)
|
||||
commit(build_purchase_request(money, payment_method_or_reference, options), options)
|
||||
end
|
||||
|
||||
def void(identification, options = {})
|
||||
commit(build_void_request(identification, options), options)
|
||||
end
|
||||
|
||||
def refund(money, identification, options = {})
|
||||
commit(build_refund_request(money, identification, options), options)
|
||||
end
|
||||
|
||||
# Adds credit to a subscription (stand alone credit).
|
||||
def credit(money, reference, options = {})
|
||||
requires!(options, :order_id)
|
||||
commit(build_credit_request(money, reference, options), options)
|
||||
end
|
||||
|
||||
# Stores a customer subscription/profile with type "on-demand".
|
||||
# To charge the card while creating a profile, pass
|
||||
# options[:setup_fee] => money
|
||||
def store(payment_method, options = {})
|
||||
requires!(options, :order_id)
|
||||
setup_address_hash(options)
|
||||
commit(build_create_subscription_request(payment_method, options), options)
|
||||
end
|
||||
|
||||
# Updates a customer subscription/profile
|
||||
def update(reference, creditcard, options = {})
|
||||
requires!(options, :order_id)
|
||||
setup_address_hash(options)
|
||||
commit(build_update_subscription_request(reference, creditcard, options), options)
|
||||
end
|
||||
|
||||
# Removes a customer subscription/profile
|
||||
def unstore(reference, options = {})
|
||||
requires!(options, :order_id)
|
||||
commit(build_delete_subscription_request(reference, options), options)
|
||||
end
|
||||
|
||||
# Retrieves a customer subscription/profile
|
||||
def retrieve(reference, options = {})
|
||||
requires!(options, :order_id)
|
||||
commit(build_retrieve_subscription_request(reference, options), options)
|
||||
end
|
||||
|
||||
# CyberSource requires that you provide line item information for tax
|
||||
# calculations. If you do not have prices for each item or want to
|
||||
# simplify the situation then pass in one fake line item that costs the
|
||||
# subtotal of the order
|
||||
#
|
||||
# The line_item hash goes in the options hash and should look like
|
||||
#
|
||||
# :line_items => [
|
||||
# {
|
||||
# :declared_value => '1',
|
||||
# :quantity => '2',
|
||||
# :code => 'default',
|
||||
# :description => 'Giant Walrus',
|
||||
# :sku => 'WA323232323232323'
|
||||
# },
|
||||
# {
|
||||
# :declared_value => '6',
|
||||
# :quantity => '1',
|
||||
# :code => 'default',
|
||||
# :description => 'Marble Snowcone',
|
||||
# :sku => 'FAKE1232132113123'
|
||||
# }
|
||||
# ]
|
||||
#
|
||||
# This functionality is only supported by this particular gateway may
|
||||
# be changed at any time
|
||||
def calculate_tax(creditcard, options)
|
||||
requires!(options, :line_items)
|
||||
setup_address_hash(options)
|
||||
commit(build_tax_calculation_request(creditcard, options), options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Create all address hash key value pairs so that we still function if we
|
||||
# were only provided with one or two of them
|
||||
def setup_address_hash(options)
|
||||
options[:billing_address] = options[:billing_address] || options[:address] || {}
|
||||
options[:shipping_address] = options[:shipping_address] || {}
|
||||
end
|
||||
|
||||
def build_auth_request(money, creditcard_or_reference, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
|
||||
add_auth_service(xml)
|
||||
add_business_rules_data(xml)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_tax_calculation_request(creditcard, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_address(xml, creditcard, options[:billing_address], options, false)
|
||||
add_address(xml, creditcard, options[:shipping_address], options, true)
|
||||
add_line_item_data(xml, options)
|
||||
add_purchase_data(xml, 0, false, options)
|
||||
add_tax_service(xml)
|
||||
add_business_rules_data(xml)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_capture_request(money, authorization, options)
|
||||
order_id, request_id, request_token = authorization.split(";")
|
||||
options[:order_id] = order_id
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_capture_service(xml, request_id, request_token)
|
||||
add_business_rules_data(xml)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_purchase_request(money, payment_method_or_reference, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
|
||||
if !payment_method_or_reference.is_a?(String) && card_brand(payment_method_or_reference) == 'check'
|
||||
add_check_service(xml)
|
||||
else
|
||||
add_purchase_service(xml, options)
|
||||
add_business_rules_data(xml)
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_void_request(identification, options)
|
||||
order_id, request_id, request_token = identification.split(";")
|
||||
options[:order_id] = order_id
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_void_service(xml, request_id, request_token)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_auth_reversal_request(money, identification, options)
|
||||
order_id, request_id, request_token = identification.split(";")
|
||||
options[:order_id] = order_id
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_auth_reversal_service(xml, request_id, request_token)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_refund_request(money, identification, options)
|
||||
order_id, request_id, request_token = identification.split(";")
|
||||
options[:order_id] = order_id
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_credit_service(xml, request_id, request_token)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_credit_request(money, reference, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_subscription(xml, options, reference)
|
||||
add_credit_service(xml)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_create_subscription_request(payment_method, options)
|
||||
options[:subscription] = (options[:subscription] || {}).merge(:frequency => "on-demand", :amount => 0, :automatic_renew => false)
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_address(xml, payment_method, options[:billing_address], options)
|
||||
add_purchase_data(xml, options[:setup_fee] || 0, true, options)
|
||||
if card_brand(payment_method) == 'check'
|
||||
add_check(xml, payment_method)
|
||||
add_check_payment_method(xml)
|
||||
add_check_service(xml, options) if options[:setup_fee]
|
||||
else
|
||||
add_creditcard(xml, payment_method)
|
||||
add_creditcard_payment_method(xml)
|
||||
add_purchase_service(xml, options) if options[:setup_fee]
|
||||
end
|
||||
add_subscription(xml, options)
|
||||
add_subscription_create_service(xml, options)
|
||||
add_business_rules_data(xml)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_update_subscription_request(reference, creditcard, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_address(xml, creditcard, options[:billing_address], options) unless options[:billing_address].blank?
|
||||
add_purchase_data(xml, options[:setup_fee], true, options) unless options[:setup_fee].blank?
|
||||
add_creditcard(xml, creditcard) if creditcard
|
||||
add_creditcard_payment_method(xml) if creditcard
|
||||
add_subscription(xml, options, reference)
|
||||
add_subscription_update_service(xml, options)
|
||||
add_business_rules_data(xml)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_delete_subscription_request(reference, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_subscription(xml, options, reference)
|
||||
add_subscription_delete_service(xml, options)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_retrieve_subscription_request(reference, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
add_subscription(xml, options, reference)
|
||||
add_subscription_retrieve_service(xml, options)
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def add_business_rules_data(xml)
|
||||
xml.tag! 'businessRules' do
|
||||
xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
|
||||
xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv]
|
||||
end
|
||||
end
|
||||
|
||||
def add_line_item_data(xml, options)
|
||||
options[:line_items].each_with_index do |value, index|
|
||||
xml.tag! 'item', {'id' => index} do
|
||||
xml.tag! 'unitPrice', amount(value[:declared_value])
|
||||
xml.tag! 'quantity', value[:quantity]
|
||||
xml.tag! 'productCode', value[:code] || 'shipping_only'
|
||||
xml.tag! 'productName', value[:description]
|
||||
xml.tag! 'productSKU', value[:sku]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_merchant_data(xml, options)
|
||||
xml.tag! 'merchantID', @options[:login]
|
||||
xml.tag! 'merchantReferenceCode', options[:order_id]
|
||||
xml.tag! 'clientLibrary' ,'Ruby Active Merchant'
|
||||
xml.tag! 'clientLibraryVersion', VERSION
|
||||
xml.tag! 'clientEnvironment' , RUBY_PLATFORM
|
||||
end
|
||||
|
||||
def add_purchase_data(xml, money = 0, include_grand_total = false, options={})
|
||||
xml.tag! 'purchaseTotals' do
|
||||
xml.tag! 'currency', options[:currency] || currency(money)
|
||||
xml.tag!('grandTotalAmount', amount(money)) if include_grand_total
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(xml, payment_method, address, options, shipTo = false)
|
||||
requires!(options, :email)
|
||||
|
||||
xml.tag! shipTo ? 'shipTo' : 'billTo' do
|
||||
xml.tag! 'firstName', payment_method.first_name if payment_method
|
||||
xml.tag! 'lastName', payment_method.last_name if payment_method
|
||||
xml.tag! 'street1', address[:address1]
|
||||
xml.tag! 'street2', address[:address2] unless address[:address2].blank?
|
||||
xml.tag! 'city', address[:city]
|
||||
xml.tag! 'state', address[:state]
|
||||
xml.tag! 'postalCode', address[:zip]
|
||||
xml.tag! 'country', address[:country]
|
||||
xml.tag! 'company', address[:company] unless address[:company].blank?
|
||||
xml.tag! 'companyTaxID', address[:companyTaxID] unless address[:company_tax_id].blank?
|
||||
xml.tag! 'phoneNumber', address[:phone_number] unless address[:phone_number].blank?
|
||||
xml.tag! 'email', options[:email]
|
||||
xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank?
|
||||
xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank?
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(xml, creditcard)
|
||||
xml.tag! 'card' do
|
||||
xml.tag! 'accountNumber', creditcard.number
|
||||
xml.tag! 'expirationMonth', format(creditcard.month, :two_digits)
|
||||
xml.tag! 'expirationYear', format(creditcard.year, :four_digits)
|
||||
xml.tag!('cvNumber', creditcard.verification_value) unless (@options[:ignore_cvv] || creditcard.verification_value.blank? )
|
||||
xml.tag! 'cardType', @@credit_card_codes[card_brand(creditcard).to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
def add_check(xml, check)
|
||||
xml.tag! 'check' do
|
||||
xml.tag! 'accountNumber', check.account_number
|
||||
xml.tag! 'accountType', check.account_type[0]
|
||||
xml.tag! 'bankTransitNumber', check.routing_number
|
||||
end
|
||||
end
|
||||
|
||||
def add_tax_service(xml)
|
||||
xml.tag! 'taxService', {'run' => 'true'} do
|
||||
xml.tag!('nexus', @options[:nexus]) unless @options[:nexus].blank?
|
||||
xml.tag!('sellerRegistration', @options[:vat_reg_number]) unless @options[:vat_reg_number].blank?
|
||||
end
|
||||
end
|
||||
|
||||
def add_auth_service(xml)
|
||||
xml.tag! 'ccAuthService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_capture_service(xml, request_id, request_token)
|
||||
xml.tag! 'ccCaptureService', {'run' => 'true'} do
|
||||
xml.tag! 'authRequestID', request_id
|
||||
xml.tag! 'authRequestToken', request_token
|
||||
end
|
||||
end
|
||||
|
||||
def add_purchase_service(xml, options)
|
||||
xml.tag! 'ccAuthService', {'run' => 'true'}
|
||||
xml.tag! 'ccCaptureService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_void_service(xml, request_id, request_token)
|
||||
xml.tag! 'voidService', {'run' => 'true'} do
|
||||
xml.tag! 'voidRequestID', request_id
|
||||
xml.tag! 'voidRequestToken', request_token
|
||||
end
|
||||
end
|
||||
|
||||
def add_auth_reversal_service(xml, request_id, request_token)
|
||||
xml.tag! 'ccAuthReversalService', {'run' => 'true'} do
|
||||
xml.tag! 'authRequestID', request_id
|
||||
xml.tag! 'authRequestToken', request_token
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_service(xml, request_id = nil, request_token = nil)
|
||||
xml.tag! 'ccCreditService', {'run' => 'true'} do
|
||||
xml.tag! 'captureRequestID', request_id if request_id
|
||||
xml.tag! 'captureRequestToken', request_token if request_token
|
||||
end
|
||||
end
|
||||
|
||||
def add_check_service(xml)
|
||||
xml.tag! 'ecDebitService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_subscription_create_service(xml, options)
|
||||
xml.tag! 'paySubscriptionCreateService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_subscription_update_service(xml, options)
|
||||
xml.tag! 'paySubscriptionUpdateService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_subscription_delete_service(xml, options)
|
||||
xml.tag! 'paySubscriptionDeleteService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_subscription_retrieve_service(xml, options)
|
||||
xml.tag! 'paySubscriptionRetrieveService', {'run' => 'true'}
|
||||
end
|
||||
|
||||
def add_subscription(xml, options, reference = nil)
|
||||
options[:subscription] ||= {}
|
||||
|
||||
xml.tag! 'recurringSubscriptionInfo' do
|
||||
if reference
|
||||
_, subscription_id, _ = reference.split(";")
|
||||
xml.tag! 'subscriptionID', subscription_id
|
||||
end
|
||||
|
||||
xml.tag! 'status', options[:subscription][:status] if options[:subscription][:status]
|
||||
xml.tag! 'amount', options[:subscription][:amount] if options[:subscription][:amount]
|
||||
xml.tag! 'numberOfPayments', options[:subscription][:occurrences] if options[:subscription][:occurrences]
|
||||
xml.tag! 'automaticRenew', options[:subscription][:automatic_renew] if options[:subscription][:automatic_renew]
|
||||
xml.tag! 'frequency', options[:subscription][:frequency] if options[:subscription][:frequency]
|
||||
xml.tag! 'startDate', options[:subscription][:start_date].strftime("%Y%m%d") if options[:subscription][:start_date]
|
||||
xml.tag! 'endDate', options[:subscription][:end_date].strftime("%Y%m%d") if options[:subscription][:end_date]
|
||||
xml.tag! 'approvalRequired', options[:subscription][:approval_required] || false
|
||||
xml.tag! 'event', options[:subscription][:event] if options[:subscription][:event]
|
||||
xml.tag! 'billPayment', options[:subscription][:bill_payment] if options[:subscription][:bill_payment]
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard_payment_method(xml)
|
||||
xml.tag! 'subscription' do
|
||||
xml.tag! 'paymentMethod', "credit card"
|
||||
end
|
||||
end
|
||||
|
||||
def add_check_payment_method(xml)
|
||||
xml.tag! 'subscription' do
|
||||
xml.tag! 'paymentMethod', "check"
|
||||
end
|
||||
end
|
||||
|
||||
def add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
|
||||
if payment_method_or_reference.is_a?(String)
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_subscription(xml, options, payment_method_or_reference)
|
||||
elsif card_brand(payment_method_or_reference) == 'check'
|
||||
add_address(xml, payment_method_or_reference, options[:billing_address], options)
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_check(xml, payment_method_or_reference)
|
||||
else
|
||||
add_address(xml, payment_method_or_reference, options[:billing_address], options)
|
||||
add_purchase_data(xml, money, true, options)
|
||||
add_creditcard(xml, payment_method_or_reference)
|
||||
end
|
||||
end
|
||||
|
||||
# Where we actually build the full SOAP request using builder
|
||||
def build_request(body, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! 's:Envelope', {'xmlns:s' => 'http://schemas.xmlsoap.org/soap/envelope/'} do
|
||||
xml.tag! 's:Header' do
|
||||
xml.tag! 'wsse:Security', {'s:mustUnderstand' => '1', 'xmlns:wsse' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'} do
|
||||
xml.tag! 'wsse:UsernameToken' do
|
||||
xml.tag! 'wsse:Username', @options[:login]
|
||||
xml.tag! 'wsse:Password', @options[:password], 'Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
|
||||
xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{XSD_VERSION}"} do
|
||||
add_merchant_data(xml, options)
|
||||
xml << body
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Contact CyberSource, make the SOAP request, and parse the reply into a
|
||||
# Response object
|
||||
def commit(request, options)
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(request, options)))
|
||||
|
||||
success = response[:decision] == "ACCEPT"
|
||||
message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
|
||||
authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken] ].compact.join(";") : nil
|
||||
|
||||
Response.new(success, message, response,
|
||||
:test => test?,
|
||||
:authorization => authorization,
|
||||
:avs_result => { :code => response[:avsCode] },
|
||||
:cvv_result => response[:cvCode]
|
||||
)
|
||||
end
|
||||
|
||||
# Parse the SOAP response
|
||||
# Technique inspired by the Paypal Gateway
|
||||
def parse(xml)
|
||||
reply = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
if root = REXML::XPath.first(xml, "//c:replyMessage")
|
||||
root.elements.to_a.each do |node|
|
||||
case node.name
|
||||
when 'c:reasonCode'
|
||||
reply[:message] = reply(node.text)
|
||||
else
|
||||
parse_element(reply, node)
|
||||
end
|
||||
end
|
||||
elsif root = REXML::XPath.first(xml, "//soap:Fault")
|
||||
parse_element(reply, root)
|
||||
reply[:message] = "#{reply[:faultcode]}: #{reply[:faultstring]}"
|
||||
end
|
||||
return reply
|
||||
end
|
||||
|
||||
def parse_element(reply, node)
|
||||
if node.has_elements?
|
||||
node.elements.each{|e| parse_element(reply, e) }
|
||||
else
|
||||
if node.parent.name =~ /item/
|
||||
parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
|
||||
reply[(parent + '_' + node.name).to_sym] = node.text
|
||||
else
|
||||
reply[node.name.to_sym] = node.text
|
||||
end
|
||||
end
|
||||
return reply
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
591
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/data_cash.rb
vendored
Normal file
591
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/data_cash.rb
vendored
Normal file
@@ -0,0 +1,591 @@
|
||||
module ActiveMerchant
|
||||
module Billing
|
||||
class DataCashGateway < Gateway
|
||||
self.default_currency = 'GBP'
|
||||
self.supported_countries = ['GB']
|
||||
|
||||
# From the DataCash docs; Page 13, the following cards are
|
||||
# usable:
|
||||
# American Express, ATM, Carte Blanche, Diners Club, Discover,
|
||||
# EnRoute, GE Capital, JCB, Laser, Maestro, Mastercard, Solo,
|
||||
# Switch, Visa, Visa Delta, VISA Electron, Visa Purchasing
|
||||
#
|
||||
# Note continuous authority is only supported for :visa, :master and :american_express card types
|
||||
self.supported_cardtypes = [ :visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro, :switch, :solo, :laser ]
|
||||
|
||||
self.homepage_url = 'http://www.datacash.com/'
|
||||
self.display_name = 'DataCash'
|
||||
|
||||
# Datacash server URLs
|
||||
self.test_url = 'https://testserver.datacash.com/Transaction'
|
||||
self.live_url = 'https://mars.transaction.datacash.com/Transaction'
|
||||
|
||||
# Different Card Transaction Types
|
||||
AUTH_TYPE = 'auth'
|
||||
CANCEL_TYPE = 'cancel'
|
||||
FULFILL_TYPE = 'fulfill'
|
||||
PRE_TYPE = 'pre'
|
||||
REFUND_TYPE = 'refund'
|
||||
TRANSACTION_REFUND_TYPE = 'txn_refund'
|
||||
|
||||
# Constant strings for use in the ExtendedPolicy complex element for
|
||||
# CV2 checks
|
||||
POLICY_ACCEPT = 'accept'
|
||||
POLICY_REJECT = 'reject'
|
||||
|
||||
# Datacash success code
|
||||
DATACASH_SUCCESS = '1'
|
||||
|
||||
# Creates a new DataCashGateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The Datacash account login.
|
||||
# * <tt>:password</tt> -- The Datacash account password.
|
||||
# * <tt>:test => +true+ or +false+</tt> -- Use the test or live Datacash url.
|
||||
#
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>authorization_or_credit_card</tt>:: The continuous authority reference or CreditCard details for the transaction.
|
||||
# * <tt>options</tt> A hash of optional parameters.
|
||||
# * <tt>:order_id</tt> A unique reference for this order (corresponds to merchantreference in datacash documentation)
|
||||
# * <tt>:set_up_continuous_authority</tt>
|
||||
# Set to true to set up a recurring historic transaction account be set up.
|
||||
# Only supported for :visa, :master and :american_express card types
|
||||
# See http://www.datacash.com/services/recurring/historic.php for more details of historic transactions.
|
||||
# * <tt>:address</tt>:: billing address for card
|
||||
#
|
||||
# The continuous authority reference will be available in response#params['ca_reference'] if you have requested one
|
||||
def purchase(money, authorization_or_credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
if authorization_or_credit_card.is_a?(String)
|
||||
request = build_purchase_or_authorization_request_with_continuous_authority_reference_request(AUTH_TYPE, money, authorization_or_credit_card, options)
|
||||
else
|
||||
request = build_purchase_or_authorization_request_with_credit_card_request(AUTH_TYPE, money, authorization_or_credit_card, options)
|
||||
end
|
||||
|
||||
commit(request)
|
||||
end
|
||||
|
||||
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
||||
# charge the card.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>authorization_or_credit_card</tt>:: The continuous authority reference or CreditCard details for the transaction.
|
||||
# * <tt>options</tt> A hash of optional parameters.
|
||||
# * <tt>:order_id</tt> A unique reference for this order (corresponds to merchantreference in datacash documentation)
|
||||
# * <tt>:set_up_continuous_authority</tt>::
|
||||
# Set to true to set up a recurring historic transaction account be set up.
|
||||
# Only supported for :visa, :master and :american_express card types
|
||||
# See http://www.datacash.com/services/recurring/historic.php for more details of historic transactions.
|
||||
# * <tt>:address</tt>:: billing address for card
|
||||
#
|
||||
# The continuous authority reference will be available in response#params['ca_reference'] if you have requested one
|
||||
def authorize(money, authorization_or_credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
if authorization_or_credit_card.is_a?(String)
|
||||
request = build_purchase_or_authorization_request_with_continuous_authority_reference_request(AUTH_TYPE, money, authorization_or_credit_card, options)
|
||||
else
|
||||
request = build_purchase_or_authorization_request_with_credit_card_request(PRE_TYPE, money, authorization_or_credit_card, options)
|
||||
end
|
||||
|
||||
commit(request)
|
||||
end
|
||||
|
||||
# Captures the funds from an authorized transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be captured as anInteger value in cents.
|
||||
# * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
|
||||
def capture(money, authorization, options = {})
|
||||
commit(build_void_or_capture_request(FULFILL_TYPE, money, authorization, options))
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous authorize request.
|
||||
def void(authorization, options = {})
|
||||
request = build_void_or_capture_request(CANCEL_TYPE, nil, authorization, options)
|
||||
|
||||
commit(request)
|
||||
end
|
||||
|
||||
# Refund to a card
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> The amount to be refunded as an Integer value in cents. Set to nil for a full refund on existing transaction.
|
||||
# * <tt>reference_or_credit_card</tt> The credit card you want to refund OR the datacash_reference for the existing transaction you are refunding
|
||||
# * <tt>options</tt> Are ignored when refunding via reference to an existing transaction, otherwise
|
||||
# * <tt>:order_id</tt> A unique reference for this order (corresponds to merchantreference in datacash documentation)
|
||||
# * <tt>:address</tt>:: billing address for card
|
||||
def credit(money, reference_or_credit_card, options = {})
|
||||
if reference_or_credit_card.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, reference_or_credit_card)
|
||||
else
|
||||
request = build_refund_request(money, reference_or_credit_card, options)
|
||||
commit(request)
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
commit(build_transaction_refund_request(money, reference))
|
||||
end
|
||||
|
||||
private
|
||||
# Create the xml document for a 'cancel' or 'fulfill' transaction.
|
||||
#
|
||||
# Final XML should look like:
|
||||
# <Request>
|
||||
# <Authentication>
|
||||
# <client>99000001</client>
|
||||
# <password>******</password>
|
||||
# </Authentication>
|
||||
# <Transaction>
|
||||
# <TxnDetails>
|
||||
# <amount>25.00</amount>
|
||||
# </TxnDetails>
|
||||
# <HistoricTxn>
|
||||
# <reference>4900200000000001</reference>
|
||||
# <authcode>A6</authcode>
|
||||
# <method>fulfill</method>
|
||||
# </HistoricTxn>
|
||||
# </Transaction>
|
||||
# </Request>
|
||||
#
|
||||
# Parameters:
|
||||
# * <tt>type</tt> must be FULFILL_TYPE or CANCEL_TYPE
|
||||
# * <tt>money</tt> - optional - Integer value in cents
|
||||
# * <tt>authorization</tt> - the Datacash authorization from a previous succesful authorize transaction
|
||||
# * <tt>options</tt>
|
||||
# * <tt>order_id</tt> - A unique reference for the transaction
|
||||
#
|
||||
# Returns:
|
||||
# -Builder xml document
|
||||
#
|
||||
def build_void_or_capture_request(type, money, authorization, options)
|
||||
reference, auth_code, ca_reference = authorization.to_s.split(';')
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! :Request do
|
||||
add_authentication(xml)
|
||||
|
||||
xml.tag! :Transaction do
|
||||
xml.tag! :HistoricTxn do
|
||||
xml.tag! :reference, reference
|
||||
xml.tag! :authcode, auth_code
|
||||
xml.tag! :method, type
|
||||
end
|
||||
|
||||
if money
|
||||
xml.tag! :TxnDetails do
|
||||
xml.tag! :merchantreference, format_reference_number(options[:order_id])
|
||||
xml.tag! :amount, amount(money), :currency => options[:currency] || currency(money)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Create the xml document for an 'auth' or 'pre' transaction with a credit card
|
||||
#
|
||||
# Final XML should look like:
|
||||
#
|
||||
# <Request>
|
||||
# <Authentication>
|
||||
# <client>99000000</client>
|
||||
# <password>*******</password>
|
||||
# </Authentication>
|
||||
# <Transaction>
|
||||
# <TxnDetails>
|
||||
# <merchantreference>123456</merchantreference>
|
||||
# <amount currency="EUR">10.00</amount>
|
||||
# </TxnDetails>
|
||||
# <CardTxn>
|
||||
# <Card>
|
||||
# <pan>4444********1111</pan>
|
||||
# <expirydate>03/04</expirydate>
|
||||
# <Cv2Avs>
|
||||
# <street_address1>Flat 7</street_address1>
|
||||
# <street_address2>89 Jumble
|
||||
# Street</street_address2>
|
||||
# <street_address3>Mytown</street_address3>
|
||||
# <postcode>AV12FR</postcode>
|
||||
# <cv2>123</cv2>
|
||||
# <ExtendedPolicy>
|
||||
# <cv2_policy notprovided="reject"
|
||||
# notchecked="accept"
|
||||
# matched="accept"
|
||||
# notmatched="reject"
|
||||
# partialmatch="reject"/>
|
||||
# <postcode_policy notprovided="reject"
|
||||
# notchecked="accept"
|
||||
# matched="accept"
|
||||
# notmatched="reject"
|
||||
# partialmatch="accept"/>
|
||||
# <address_policy notprovided="reject"
|
||||
# notchecked="accept"
|
||||
# matched="accept"
|
||||
# notmatched="reject"
|
||||
# partialmatch="accept"/>
|
||||
# </ExtendedPolicy>
|
||||
# </Cv2Avs>
|
||||
# </Card>
|
||||
# <method>auth</method>
|
||||
# </CardTxn>
|
||||
# </Transaction>
|
||||
# </Request>
|
||||
#
|
||||
# Parameters:
|
||||
# -type must be 'auth' or 'pre'
|
||||
# -money - A money object with the price and currency
|
||||
# -credit_card - The credit_card details to use
|
||||
# -options:
|
||||
# :order_id is the merchant reference number
|
||||
# :billing_address is the billing address for the cc
|
||||
# :address is the delivery address
|
||||
#
|
||||
# Returns:
|
||||
# -xml: Builder document containing the markup
|
||||
#
|
||||
def build_purchase_or_authorization_request_with_credit_card_request(type, money, credit_card, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! :Request do
|
||||
add_authentication(xml)
|
||||
|
||||
xml.tag! :Transaction do
|
||||
if options[:set_up_continuous_authority]
|
||||
xml.tag! :ContAuthTxn, :type => 'setup'
|
||||
end
|
||||
xml.tag! :CardTxn do
|
||||
xml.tag! :method, type
|
||||
add_credit_card(xml, credit_card, options[:billing_address])
|
||||
end
|
||||
xml.tag! :TxnDetails do
|
||||
xml.tag! :merchantreference, format_reference_number(options[:order_id])
|
||||
xml.tag! :amount, amount(money), :currency => options[:currency] || currency(money)
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Create the xml document for an 'auth' or 'pre' transaction with
|
||||
# continuous authorization
|
||||
#
|
||||
# Final XML should look like:
|
||||
#
|
||||
# <Request>
|
||||
# <Transaction>
|
||||
# <ContAuthTxn type="historic" />
|
||||
# <TxnDetails>
|
||||
# <merchantreference>3851231</merchantreference>
|
||||
# <capturemethod>cont_auth</capturemethod>
|
||||
# <amount currency="GBP">18.50</amount>
|
||||
# </TxnDetails>
|
||||
# <HistoricTxn>
|
||||
# <reference>4500200040925092</reference>
|
||||
# <method>auth</method>
|
||||
# </HistoricTxn>
|
||||
# </Transaction>
|
||||
# <Authentication>
|
||||
# <client>99000001</client>
|
||||
# <password>mypasswd</password>
|
||||
# </Authentication>
|
||||
# </Request>
|
||||
#
|
||||
# Parameters:
|
||||
# -type must be 'auth' or 'pre'
|
||||
# -money - A money object with the price and currency
|
||||
# -authorization - The authorization containing a continuous authority reference previously set up on a credit card
|
||||
# -options:
|
||||
# :order_id is the merchant reference number
|
||||
#
|
||||
# Returns:
|
||||
# -xml: Builder document containing the markup
|
||||
#
|
||||
def build_purchase_or_authorization_request_with_continuous_authority_reference_request(type, money, authorization, options)
|
||||
reference, auth_code, ca_reference = authorization.to_s.split(';')
|
||||
raise ArgumentError, "The continuous authority reference is required for continuous authority transactions" if ca_reference.blank?
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! :Request do
|
||||
add_authentication(xml)
|
||||
xml.tag! :Transaction do
|
||||
xml.tag! :ContAuthTxn, :type => 'historic'
|
||||
xml.tag! :HistoricTxn do
|
||||
xml.tag! :reference, ca_reference
|
||||
xml.tag! :method, type
|
||||
end
|
||||
xml.tag! :TxnDetails do
|
||||
xml.tag! :merchantreference, format_reference_number(options[:order_id])
|
||||
xml.tag! :amount, amount(money), :currency => options[:currency] || currency(money)
|
||||
xml.tag! :capturemethod, 'cont_auth'
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Create the xml document for a full or partial refund transaction with
|
||||
#
|
||||
# Final XML should look like:
|
||||
#
|
||||
# <Request>
|
||||
# <Authentication>
|
||||
# <client>99000001</client>
|
||||
# <password>*******</password>
|
||||
# </Authentication>
|
||||
# <Transaction>
|
||||
# <HistoricTxn>
|
||||
# <method>txn_refund</method>
|
||||
# <reference>12345678</reference>
|
||||
# </HistoricTxn>
|
||||
# <TxnDetails>
|
||||
# <amount>10.00</amount>
|
||||
# </TxnDetails>
|
||||
# </Transaction>
|
||||
# </Request>
|
||||
#
|
||||
def build_transaction_refund_request(money, reference)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! :Request do
|
||||
add_authentication(xml)
|
||||
xml.tag! :Transaction do
|
||||
xml.tag! :HistoricTxn do
|
||||
xml.tag! :reference, reference
|
||||
xml.tag! :method, TRANSACTION_REFUND_TYPE
|
||||
end
|
||||
unless money.nil?
|
||||
xml.tag! :TxnDetails do
|
||||
xml.tag! :amount, amount(money)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# Create the xml document for a full or partial refund with
|
||||
#
|
||||
# Final XML should look like:
|
||||
#
|
||||
# <Request>
|
||||
# <Authentication>
|
||||
# <client>99000001</client>
|
||||
# <password>*****</password>
|
||||
# </Authentication>
|
||||
# <Transaction>
|
||||
# <CardTxn>
|
||||
# <Card>
|
||||
# <pan>633300*********1</pan>
|
||||
# <expirydate>04/06</expirydate>
|
||||
# <startdate>01/04</startdate>
|
||||
# </Card>
|
||||
# <method>refund</method>
|
||||
# </CardTxn>
|
||||
# <TxnDetails>
|
||||
# <merchantreference>1000001</merchantreference>
|
||||
# <amount currency="GBP">95.99</amount>
|
||||
# </TxnDetails>
|
||||
# </Transaction>
|
||||
# </Request>
|
||||
def build_refund_request(money, credit_card, options)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! :Request do
|
||||
add_authentication(xml)
|
||||
xml.tag! :Transaction do
|
||||
xml.tag! :CardTxn do
|
||||
xml.tag! :method, REFUND_TYPE
|
||||
add_credit_card(xml, credit_card, options[:billing_address])
|
||||
end
|
||||
xml.tag! :TxnDetails do
|
||||
xml.tag! :merchantreference, format_reference_number(options[:order_id])
|
||||
xml.tag! :amount, amount(money)
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
|
||||
# Adds the authentication element to the passed builder xml doc
|
||||
#
|
||||
# Parameters:
|
||||
# -xml: Builder document that is being built up
|
||||
#
|
||||
# Returns:
|
||||
# -none: The results is stored in the passed xml document
|
||||
#
|
||||
def add_authentication(xml)
|
||||
xml.tag! :Authentication do
|
||||
xml.tag! :client, @options[:login]
|
||||
xml.tag! :password, @options[:password]
|
||||
end
|
||||
end
|
||||
|
||||
# Add credit_card details to the passed XML Builder doc
|
||||
#
|
||||
# Parameters:
|
||||
# -xml: Builder document that is being built up
|
||||
# -credit_card: ActiveMerchant::Billing::CreditCard object
|
||||
# -billing_address: Hash containing all of the billing address details
|
||||
#
|
||||
# Returns:
|
||||
# -none: The results is stored in the passed xml document
|
||||
#
|
||||
def add_credit_card(xml, credit_card, address)
|
||||
|
||||
xml.tag! :Card do
|
||||
|
||||
# DataCash calls the CC number 'pan'
|
||||
xml.tag! :pan, credit_card.number
|
||||
xml.tag! :expirydate, format_date(credit_card.month, credit_card.year)
|
||||
|
||||
# optional values - for Solo etc
|
||||
if [ 'switch', 'solo' ].include?(card_brand(credit_card).to_s)
|
||||
|
||||
xml.tag! :issuenumber, credit_card.issue_number unless credit_card.issue_number.blank?
|
||||
|
||||
if !credit_card.start_month.blank? && !credit_card.start_year.blank?
|
||||
xml.tag! :startdate, format_date(credit_card.start_month, credit_card.start_year)
|
||||
end
|
||||
end
|
||||
|
||||
xml.tag! :Cv2Avs do
|
||||
xml.tag! :cv2, credit_card.verification_value if credit_card.verification_value?
|
||||
if address
|
||||
xml.tag! :street_address1, address[:address1] unless address[:address1].blank?
|
||||
xml.tag! :street_address2, address[:address2] unless address[:address2].blank?
|
||||
xml.tag! :street_address3, address[:address3] unless address[:address3].blank?
|
||||
xml.tag! :street_address4, address[:address4] unless address[:address4].blank?
|
||||
xml.tag! :postcode, address[:zip] unless address[:zip].blank?
|
||||
end
|
||||
|
||||
# The ExtendedPolicy defines what to do when the passed data
|
||||
# matches, or not...
|
||||
#
|
||||
# All of the following elements MUST be present for the
|
||||
# xml to be valid (or can drop the ExtendedPolicy and use
|
||||
# a predefined one
|
||||
xml.tag! :ExtendedPolicy do
|
||||
xml.tag! :cv2_policy,
|
||||
:notprovided => POLICY_REJECT,
|
||||
:notchecked => POLICY_REJECT,
|
||||
:matched => POLICY_ACCEPT,
|
||||
:notmatched => POLICY_REJECT,
|
||||
:partialmatch => POLICY_REJECT
|
||||
xml.tag! :postcode_policy,
|
||||
:notprovided => POLICY_ACCEPT,
|
||||
:notchecked => POLICY_ACCEPT,
|
||||
:matched => POLICY_ACCEPT,
|
||||
:notmatched => POLICY_REJECT,
|
||||
:partialmatch => POLICY_ACCEPT
|
||||
xml.tag! :address_policy,
|
||||
:notprovided => POLICY_ACCEPT,
|
||||
:notchecked => POLICY_ACCEPT,
|
||||
:matched => POLICY_ACCEPT,
|
||||
:notmatched => POLICY_REJECT,
|
||||
:partialmatch => POLICY_ACCEPT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Send the passed data to DataCash for processing
|
||||
#
|
||||
# Parameters:
|
||||
# -request: The XML data that is to be sent to Datacash
|
||||
#
|
||||
# Returns:
|
||||
# - ActiveMerchant::Billing::Response object
|
||||
#
|
||||
def commit(request)
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, request))
|
||||
|
||||
Response.new(response[:status] == DATACASH_SUCCESS, response[:reason], response,
|
||||
:test => test?,
|
||||
:authorization => "#{response[:datacash_reference]};#{response[:authcode]};#{response[:ca_reference]}"
|
||||
)
|
||||
end
|
||||
|
||||
# Returns a date string in the format Datacash expects
|
||||
#
|
||||
# Parameters:
|
||||
# -month: integer, the month
|
||||
# -year: integer, the year
|
||||
#
|
||||
# Returns:
|
||||
# -String: date in MM/YY format
|
||||
#
|
||||
def format_date(month, year)
|
||||
"#{format(month,:two_digits)}/#{format(year, :two_digits)}"
|
||||
end
|
||||
|
||||
# Parse the datacash response and create a Response object
|
||||
#
|
||||
# Parameters:
|
||||
# -body: The XML returned from Datacash
|
||||
#
|
||||
# Returns:
|
||||
# -a hash with all of the values returned in the Datacash XML response
|
||||
#
|
||||
def parse(body)
|
||||
|
||||
response = {}
|
||||
xml = REXML::Document.new(body)
|
||||
root = REXML::XPath.first(xml, "//Response")
|
||||
|
||||
root.elements.to_a.each do |node|
|
||||
parse_element(response, node)
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# Parse an xml element
|
||||
#
|
||||
# Parameters:
|
||||
# -response: The hash that the values are being returned in
|
||||
# -node: The node that is currently being read
|
||||
#
|
||||
# Returns:
|
||||
# - none (results are stored in the passed hash)
|
||||
def parse_element(response, node)
|
||||
if node.has_elements?
|
||||
node.elements.each{|e| parse_element(response, e) }
|
||||
else
|
||||
response[node.name.underscore.to_sym] = node.text
|
||||
end
|
||||
end
|
||||
|
||||
def format_reference_number(number)
|
||||
number.to_s.gsub(/[^A-Za-z0-9]/, '').rjust(6, "0").first(30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
230
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/efsnet.rb
vendored
Normal file
230
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/efsnet.rb
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
require 'rexml/document'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
|
||||
class EfsnetGateway < Gateway
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.homepage_url = 'http://www.concordefsnet.com/'
|
||||
self.display_name = 'Efsnet'
|
||||
|
||||
self.test_url = 'https://testefsnet.concordebiz.com/efsnet.dll'
|
||||
self.live_url = 'https://efsnet.concordebiz.com/efsnet.dll'
|
||||
|
||||
# login is your Store ID
|
||||
# password is your Store Key
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
request = build_credit_card_request(money, creditcard, options)
|
||||
commit(:credit_card_authorize, request)
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
request = build_credit_card_request(money, creditcard, options)
|
||||
commit(:credit_card_charge, request)
|
||||
end
|
||||
|
||||
def capture(money, identification, options = {})
|
||||
request = build_refund_or_settle_request(money, identification, options)
|
||||
commit(:credit_card_settle, request)
|
||||
end
|
||||
|
||||
def credit(money, identification_or_credit_card, options = {})
|
||||
if identification_or_credit_card.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
# Perform authorization reversal
|
||||
refund(money, identification_or_credit_card, options)
|
||||
else
|
||||
# Perform credit
|
||||
request = build_credit_card_request(money, identification_or_credit_card, options)
|
||||
commit(:credit_card_credit, request)
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
# Perform authorization reversal
|
||||
request = build_refund_or_settle_request(money, reference, options)
|
||||
commit(:credit_card_refund, request)
|
||||
end
|
||||
|
||||
def void(identification, options = {})
|
||||
requires!(options, :order_id)
|
||||
original_transaction_id, original_transaction_amount = identification.split(";")
|
||||
commit(:void_transaction, {:reference_number => format_reference_number(options[:order_id]), :transaction_id => original_transaction_id})
|
||||
end
|
||||
|
||||
def voice_authorize(money, authorization_code, creditcard, options = {})
|
||||
options[:authorization_number] = authorization_code
|
||||
request = build_credit_card_request(money, creditcard, options)
|
||||
commit(:credit_card_voice_authorize, request)
|
||||
end
|
||||
|
||||
def force(money, authorization_code, creditcard, options = {})
|
||||
options[:authorization_number] = authorization_code
|
||||
request = build_credit_card_request(money, creditcard, options)
|
||||
commit(:credit_card_capture, request)
|
||||
end
|
||||
|
||||
def system_check
|
||||
commit(:system_check, {})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_refund_or_settle_request(money, identification, options = {})
|
||||
original_transaction_id, original_transaction_amount = identification.split(";")
|
||||
|
||||
requires!(options, :order_id)
|
||||
|
||||
post = {
|
||||
:reference_number => format_reference_number(options[:order_id]),
|
||||
:transaction_amount => amount(money),
|
||||
:original_transaction_amount => original_transaction_amount,
|
||||
:original_transaction_id => original_transaction_id,
|
||||
:client_ip_address => options[:ip]
|
||||
}
|
||||
end
|
||||
|
||||
def build_credit_card_request(money, creditcard, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
post = {
|
||||
:reference_number => format_reference_number(options[:order_id]),
|
||||
:authorization_number => options[:authorization_number],
|
||||
:transaction_amount => amount(money),
|
||||
:client_ip_address => options[:ip]
|
||||
|
||||
}
|
||||
add_creditcard(post,creditcard)
|
||||
add_address(post,options)
|
||||
post
|
||||
end
|
||||
|
||||
def format_reference_number(number)
|
||||
number.to_s.slice(0,12)
|
||||
end
|
||||
|
||||
def add_address(post,options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
if address[:address2]
|
||||
post[:billing_address] = address[:address1].to_s << ' ' << address[:address2].to_s
|
||||
else
|
||||
post[:billing_address] = address[:address1].to_s
|
||||
end
|
||||
post[:billing_city] = address[:city].to_s
|
||||
post[:billing_state] = address[:state].blank? ? 'n/a' : address[:state]
|
||||
post[:billing_postal_code] = address[:zip].to_s
|
||||
post[:billing_country] = address[:country].to_s
|
||||
end
|
||||
|
||||
if address = options[:shipping_address]
|
||||
if address[:address2]
|
||||
post[:shipping_address] = address[:address1].to_s << ' ' << address[:address2].to_s
|
||||
else
|
||||
post[:shipping_address] = address[:address1].to_s
|
||||
end
|
||||
post[:shipping_city] = address[:city].to_s
|
||||
post[:shipping_state] = address[:state].blank? ? 'n/a' : address[:state]
|
||||
post[:shipping_postal_code] = address[:zip].to_s
|
||||
post[:shipping_country] = address[:country].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:billing_name] = creditcard.name if creditcard.name
|
||||
post[:account_number] = creditcard.number
|
||||
post[:card_verification_value] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:expiration_month] = sprintf("%.2i", creditcard.month)
|
||||
post[:expiration_year] = sprintf("%.4i", creditcard.year)[-2..-1]
|
||||
end
|
||||
|
||||
|
||||
def commit(action, parameters)
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, post_data(action, parameters), 'Content-Type' => 'text/xml'))
|
||||
|
||||
Response.new(success?(response), message_from(response[:result_message]), response,
|
||||
:test => test?,
|
||||
:authorization => authorization_from(response, parameters),
|
||||
:avs_result => { :code => response[:avs_response_code] },
|
||||
:cvv_result => response[:cvv_response_code]
|
||||
)
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response[:response_code] == '0'
|
||||
end
|
||||
|
||||
def authorization_from(response, params)
|
||||
[ response[:transaction_id], params[:transaction_amount] ].compact.join(';')
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
|
||||
xml = REXML::Document.new(xml)
|
||||
|
||||
xml.elements.each('//Reply//TransactionReply/*') do |node|
|
||||
|
||||
response[node.name.underscore.to_sym] = normalize(node.text)
|
||||
|
||||
end unless xml.root.nil?
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
xml = REXML::Document.new("<?xml version='1.0' encoding='UTF-8'?>")
|
||||
root = xml.add_element("Request")
|
||||
root.attributes["StoreID"] = options[:login]
|
||||
root.attributes["StoreKey"] = options[:password]
|
||||
root.attributes["ApplicationID"] = 'ot 1.0'
|
||||
transaction = root.add_element(action.to_s.camelize)
|
||||
|
||||
actions[action].each do |key|
|
||||
transaction.add_element(key).text = parameters[key.underscore.to_sym] unless parameters[key.underscore.to_sym].blank?
|
||||
end
|
||||
|
||||
xml.to_s
|
||||
end
|
||||
|
||||
def message_from(message)
|
||||
return 'Unspecified error' if message.blank?
|
||||
message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
|
||||
end
|
||||
|
||||
# Make a ruby type out of the response string
|
||||
def normalize(field)
|
||||
case field
|
||||
when "true" then true
|
||||
when "false" then false
|
||||
when "" then nil
|
||||
when "null" then nil
|
||||
else field
|
||||
end
|
||||
end
|
||||
|
||||
def actions
|
||||
ACTIONS
|
||||
end
|
||||
|
||||
CREDIT_CARD_FIELDS = %w(AuthorizationNumber ClientIpAddress BillingAddress BillingCity BillingState BillingPostalCode BillingCountry BillingName CardVerificationValue ExpirationMonth ExpirationYear ReferenceNumber TransactionAmount AccountNumber )
|
||||
|
||||
ACTIONS = {
|
||||
:credit_card_authorize => CREDIT_CARD_FIELDS,
|
||||
:credit_card_charge => CREDIT_CARD_FIELDS,
|
||||
:credit_card_voice_authorize => CREDIT_CARD_FIELDS,
|
||||
:credit_card_capture => CREDIT_CARD_FIELDS,
|
||||
:credit_card_credit => CREDIT_CARD_FIELDS + ["OriginalTransactionAmount"],
|
||||
:credit_card_refund => %w(ReferenceNumber TransactionAmount OriginalTransactionAmount OriginalTransactionID ClientIpAddress),
|
||||
:void_transaction => %w(ReferenceNumber TransactionID),
|
||||
:credit_card_settle => %w(ReferenceNumber TransactionAmount OriginalTransactionAmount OriginalTransactionID ClientIpAddress),
|
||||
:system_check => %w(SystemCheck),
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
312
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/elavon.rb
vendored
Normal file
312
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/elavon.rb
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
require File.dirname(__FILE__) + '/viaklix'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# = Elavon Virtual Merchant Gateway
|
||||
#
|
||||
# == Example use:
|
||||
#
|
||||
# gateway = ActiveMerchant::Billing::ElavonGateway.new(
|
||||
# :login => "my_virtual_merchant_id",
|
||||
# :password => "my_virtual_merchant_pin",
|
||||
# :user => "my_virtual_merchant_user_id" # optional
|
||||
# )
|
||||
#
|
||||
# # set up credit card obj as in main ActiveMerchant example
|
||||
# creditcard = ActiveMerchant::Billing::CreditCard.new(
|
||||
# :type => 'visa',
|
||||
# :number => '41111111111111111',
|
||||
# :month => 10,
|
||||
# :year => 2011,
|
||||
# :first_name => 'Bob',
|
||||
# :last_name => 'Bobsen'
|
||||
# )
|
||||
#
|
||||
# # run request
|
||||
# response = gateway.purchase(1000, creditcard) # authorize and capture 10 USD
|
||||
#
|
||||
# puts response.success? # Check whether the transaction was successful
|
||||
# puts response.message # Retrieve the message returned by Elavon
|
||||
# puts response.authorization # Retrieve the unique transaction ID returned by Elavon
|
||||
#
|
||||
class ElavonGateway < Gateway
|
||||
class_attribute :test_url, :live_url, :delimiter, :actions
|
||||
|
||||
self.test_url = 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/process.do'
|
||||
self.live_url = 'https://www.myvirtualmerchant.com/VirtualMerchant/process.do'
|
||||
|
||||
self.display_name = 'Elavon MyVirtualMerchant'
|
||||
self.supported_countries = ['US', 'CA']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.homepage_url = 'http://www.elavon.com/'
|
||||
|
||||
self.delimiter = "\n"
|
||||
self.actions = {
|
||||
:purchase => 'CCSALE',
|
||||
:credit => 'CCCREDIT',
|
||||
:refund => 'CCRETURN',
|
||||
:authorize => 'CCAUTHONLY',
|
||||
:capture => 'CCFORCE',
|
||||
:void => 'CCVOID'
|
||||
}
|
||||
|
||||
# Initialize the Gateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- Merchant ID
|
||||
# * <tt>:password</tt> -- PIN
|
||||
# * <tt>:user</tt> -- Specify a subuser of the account (optional)
|
||||
# * <tt>:test => +true+ or +false+</tt> -- Force test transactions
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Make a purchase
|
||||
def purchase(money, creditcard, options = {})
|
||||
form = {}
|
||||
add_salestax(form, options)
|
||||
add_invoice(form, options)
|
||||
add_creditcard(form, creditcard)
|
||||
add_address(form, options)
|
||||
add_customer_data(form, options)
|
||||
add_test_mode(form, options)
|
||||
commit(:purchase, money, form)
|
||||
end
|
||||
|
||||
# Authorize a credit card for a given amount.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> - The CreditCard details for the transaction.
|
||||
# * <tt>options</tt>
|
||||
# * <tt>:billing_address</tt> - The billing address for the cardholder.
|
||||
def authorize(money, creditcard, options = {})
|
||||
form = {}
|
||||
add_salestax(form, options)
|
||||
add_invoice(form, options)
|
||||
add_creditcard(form, creditcard)
|
||||
add_address(form, options)
|
||||
add_customer_data(form, options)
|
||||
add_test_mode(form, options)
|
||||
commit(:authorize, money, form)
|
||||
end
|
||||
|
||||
# Capture authorized funds from a credit card.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be captured as an Integer value in cents.
|
||||
# * <tt>authorization</tt> - The approval code returned from the initial authorization.
|
||||
# * <tt>options</tt>
|
||||
# * <tt>:credit_card</tt> - The CreditCard details from the initial transaction (required).
|
||||
def capture(money, authorization, options = {})
|
||||
requires!(options, :credit_card)
|
||||
|
||||
form = {}
|
||||
add_salestax(form, options)
|
||||
add_approval_code(form, authorization)
|
||||
add_invoice(form, options)
|
||||
add_creditcard(form, options[:credit_card])
|
||||
add_customer_data(form, options)
|
||||
add_test_mode(form, options)
|
||||
commit(:capture, money, form)
|
||||
end
|
||||
|
||||
# Refund a transaction.
|
||||
#
|
||||
# This transaction indicates to the gateway that
|
||||
# money should flow from the merchant to the customer.
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
||||
# * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
|
||||
# * <tt>options</tt> -- A hash of parameters.
|
||||
def refund(money, identification, options = {})
|
||||
form = {}
|
||||
add_txn_id(form, identification)
|
||||
add_test_mode(form, options)
|
||||
commit(:refund, money, form)
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous request.
|
||||
def void(identification, options = {})
|
||||
form = {}
|
||||
add_txn_id(form, identification)
|
||||
add_test_mode(form, options)
|
||||
commit(:void, nil, form)
|
||||
end
|
||||
|
||||
# Make a credit to a card. Use the refund method if you'd like to credit using
|
||||
# previous transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be credited as an Integer value in cents.
|
||||
# * <tt>creditcard</tt> - The credit card to be credited.
|
||||
# * <tt>options</tt>
|
||||
def credit(money, creditcard, options = {})
|
||||
if creditcard.is_a?(String)
|
||||
raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
|
||||
end
|
||||
|
||||
form = {}
|
||||
add_invoice(form, options)
|
||||
add_creditcard(form, creditcard)
|
||||
add_address(form, options)
|
||||
add_customer_data(form, options)
|
||||
add_test_mode(form, options)
|
||||
commit(:credit, money, form)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def add_invoice(form,options)
|
||||
form[:invoice_number] = (options[:order_id] || options[:invoice]).to_s.slice(0, 10)
|
||||
form[:description] = options[:description].to_s.slice(0, 255)
|
||||
end
|
||||
|
||||
def add_approval_code(form, authorization)
|
||||
form[:approval_code] = authorization.split(';').first
|
||||
end
|
||||
|
||||
def add_txn_id(form, authorization)
|
||||
form[:txn_id] = authorization.split(';').last
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
[response['approval_code'], response['txn_id']].join(';')
|
||||
end
|
||||
|
||||
def add_creditcard(form, creditcard)
|
||||
form[:card_number] = creditcard.number
|
||||
form[:exp_date] = expdate(creditcard)
|
||||
|
||||
if creditcard.verification_value?
|
||||
add_verification_value(form, creditcard)
|
||||
end
|
||||
|
||||
form[:first_name] = creditcard.first_name.to_s.slice(0, 20)
|
||||
form[:last_name] = creditcard.last_name.to_s.slice(0, 30)
|
||||
end
|
||||
|
||||
def add_verification_value(form, creditcard)
|
||||
form[:cvv2cvc2] = creditcard.verification_value
|
||||
form[:cvv2cvc2_indicator] = '1'
|
||||
end
|
||||
|
||||
def add_customer_data(form, options)
|
||||
form[:email] = options[:email].to_s.slice(0, 100) unless options[:email].blank?
|
||||
form[:customer_code] = options[:customer].to_s.slice(0, 10) unless options[:customer].blank?
|
||||
end
|
||||
|
||||
def add_salestax(form, options)
|
||||
form[:salestax] = options[:tax] if options[:tax].present?
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = sprintf("%.4i", creditcard.year)
|
||||
month = sprintf("%.2i", creditcard.month)
|
||||
"#{month}#{year[2..3]}"
|
||||
end
|
||||
|
||||
def add_address(form,options)
|
||||
billing_address = options[:billing_address] || options[:address]
|
||||
|
||||
if billing_address
|
||||
form[:avs_address] = billing_address[:address1].to_s.slice(0, 30)
|
||||
form[:address2] = billing_address[:address2].to_s.slice(0, 30)
|
||||
form[:avs_zip] = billing_address[:zip].to_s.slice(0, 10)
|
||||
form[:city] = billing_address[:city].to_s.slice(0, 30)
|
||||
form[:state] = billing_address[:state].to_s.slice(0, 10)
|
||||
form[:company] = billing_address[:company].to_s.slice(0, 50)
|
||||
form[:phone] = billing_address[:phone].to_s.slice(0, 20)
|
||||
form[:country] = billing_address[:country].to_s.slice(0, 50)
|
||||
end
|
||||
|
||||
if shipping_address = options[:shipping_address]
|
||||
first_name, last_name = parse_first_and_last_name(shipping_address[:name])
|
||||
form[:ship_to_first_name] = first_name.to_s.slice(0, 20)
|
||||
form[:ship_to_last_name] = last_name.to_s.slice(0, 30)
|
||||
form[:ship_to_address1] = shipping_address[:address1].to_s.slice(0, 30)
|
||||
form[:ship_to_address2] = shipping_address[:address2].to_s.slice(0, 30)
|
||||
form[:ship_to_city] = shipping_address[:city].to_s.slice(0, 30)
|
||||
form[:ship_to_state] = shipping_address[:state].to_s.slice(0, 10)
|
||||
form[:ship_to_company] = shipping_address[:company].to_s.slice(0, 50)
|
||||
form[:ship_to_country] = shipping_address[:country].to_s.slice(0, 50)
|
||||
form[:ship_to_zip] = shipping_address[:zip].to_s.slice(0, 10)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_first_and_last_name(value)
|
||||
name = value.to_s.split(' ')
|
||||
|
||||
last_name = name.pop || ''
|
||||
first_name = name.join(' ')
|
||||
[ first_name, last_name ]
|
||||
end
|
||||
|
||||
def add_test_mode(form, options)
|
||||
form[:test_mode] = 'TRUE' if options[:test_mode]
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
success?(response) ? response['result_message'] : response['errorMessage']
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
!response.has_key?('errorMessage')
|
||||
end
|
||||
|
||||
def commit(action, money, parameters)
|
||||
parameters[:amount] = amount(money)
|
||||
parameters[:transaction_type] = self.actions[action]
|
||||
|
||||
response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters)) )
|
||||
|
||||
Response.new(response['result'] == '0', message_from(response), response,
|
||||
:test => @options[:test] || test?,
|
||||
:authorization => authorization_from(response),
|
||||
:avs_result => { :code => response['avs_response'] },
|
||||
:cvv_result => response['cvv2_response']
|
||||
)
|
||||
end
|
||||
|
||||
def post_data(parameters)
|
||||
result = preamble
|
||||
result.merge!(parameters)
|
||||
result.collect { |key, value| "ssl_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
def preamble
|
||||
result = {
|
||||
'merchant_id' => @options[:login],
|
||||
'pin' => @options[:password],
|
||||
'show_form' => 'false',
|
||||
'result_format' => 'ASCII'
|
||||
}
|
||||
|
||||
result['user_id'] = @options[:user] unless @options[:user].blank?
|
||||
result
|
||||
end
|
||||
|
||||
def parse(msg)
|
||||
resp = {}
|
||||
msg.split(self.delimiter).collect{|li|
|
||||
key, value = li.split("=")
|
||||
resp[key.strip.gsub(/^ssl_/, '')] = value.to_s.strip
|
||||
}
|
||||
resp
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
275
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/epay.rb
vendored
Normal file
275
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/epay.rb
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class EpayGateway < Gateway
|
||||
API_HOST = 'ssl.ditonlinebetalingssystem.dk'
|
||||
self.live_url = 'https://' + API_HOST + '/remote/payment'
|
||||
|
||||
self.default_currency = 'DKK'
|
||||
self.money_format = :cents
|
||||
self.supported_cardtypes = [:dankort, :forbrugsforeningen, :visa, :master,
|
||||
:american_express, :diners_club, :jcb, :maestro]
|
||||
self.supported_countries = ['DK', 'SE', 'NO']
|
||||
self.homepage_url = 'http://epay.dk/'
|
||||
self.display_name = 'ePay'
|
||||
|
||||
CURRENCY_CODES = {
|
||||
:ADP => '020', :AED => '784', :AFA => '004', :ALL => '008', :AMD => '051',
|
||||
:ANG => '532', :AOA => '973', :ARS => '032', :AUD => '036', :AWG => '533',
|
||||
:AZM => '031', :BAM => '977', :BBD => '052', :BDT => '050', :BGL => '100',
|
||||
:BGN => '975', :BHD => '048', :BIF => '108', :BMD => '060', :BND => '096',
|
||||
:BOB => '068', :BOV => '984', :BRL => '986', :BSD => '044', :BTN => '064',
|
||||
:BWP => '072', :BYR => '974', :BZD => '084', :CAD => '124', :CDF => '976',
|
||||
:CHF => '756', :CLF => '990', :CLP => '152', :CNY => '156', :COP => '170',
|
||||
:CRC => '188', :CUP => '192', :CVE => '132', :CYP => '196', :CZK => '203',
|
||||
:DJF => '262', :DKK => '208', :DOP => '214', :DZD => '012', :ECS => '218',
|
||||
:ECV => '983', :EEK => '233', :EGP => '818', :ERN => '232', :ETB => '230',
|
||||
:EUR => '978', :FJD => '242', :FKP => '238', :GBP => '826', :GEL => '981',
|
||||
:GHC => '288', :GIP => '292', :GMD => '270', :GNF => '324', :GTQ => '320',
|
||||
:GWP => '624', :GYD => '328', :HKD => '344', :HNL => '340', :HRK => '191',
|
||||
:HTG => '332', :HUF => '348', :IDR => '360', :ILS => '376', :INR => '356',
|
||||
:IQD => '368', :IRR => '364', :ISK => '352', :JMD => '388', :JOD => '400',
|
||||
:JPY => '392', :KES => '404', :KGS => '417', :KHR => '116', :KMF => '174',
|
||||
:KPW => '408', :KRW => '410', :KWD => '414', :KYD => '136', :KZT => '398',
|
||||
:LAK => '418', :LBP => '422', :LKR => '144', :LRD => '430', :LSL => '426',
|
||||
:LTL => '440', :LVL => '428', :LYD => '434', :MAD => '504', :MDL => '498',
|
||||
:MGF => '450', :MKD => '807', :MMK => '104', :MNT => '496', :MOP => '446',
|
||||
:MRO => '478', :MTL => '470', :MUR => '480', :MVR => '462', :MWK => '454',
|
||||
:MXN => '484', :MXV => '979', :MYR => '458', :MZM => '508', :NAD => '516',
|
||||
:NGN => '566', :NIO => '558', :NOK => '578', :NPR => '524', :NZD => '554',
|
||||
:OMR => '512', :PAB => '590', :PEN => '604', :PGK => '598', :PHP => '608',
|
||||
:PKR => '586', :PLN => '985', :PYG => '600', :QAR => '634', :ROL => '642',
|
||||
:RUB => '643', :RUR => '810', :RWF => '646', :SAR => '682', :SBD => '090',
|
||||
:SCR => '690', :SDD => '736', :SEK => '752', :SGD => '702', :SHP => '654',
|
||||
:SIT => '705', :SKK => '703', :SLL => '694', :SOS => '706', :SRG => '740',
|
||||
:STD => '678', :SVC => '222', :SYP => '760', :SZL => '748', :THB => '764',
|
||||
:TJS => '972', :TMM => '795', :TND => '788', :TOP => '776', :TPE => '626',
|
||||
:TRL => '792', :TRY => '949', :TTD => '780', :TWD => '901', :TZS => '834',
|
||||
:UAH => '980', :UGX => '800', :USD => '840', :UYU => '858', :UZS => '860',
|
||||
:VEB => '862', :VND => '704', :VUV => '548', :XAF => '950', :XCD => '951',
|
||||
:XOF => '952', :XPF => '953', :YER => '886', :YUM => '891', :ZAR => '710',
|
||||
:ZMK => '894', :ZWD => '716'
|
||||
}
|
||||
|
||||
# login: merchant number
|
||||
# password: referrer url (for authorize authentication)
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, credit_card_or_reference, options = {})
|
||||
post = {}
|
||||
|
||||
add_amount(post, money, options)
|
||||
add_invoice(post, options)
|
||||
add_creditcard_or_reference(post, credit_card_or_reference)
|
||||
add_instant_capture(post, false)
|
||||
|
||||
commit(:authorize, post)
|
||||
end
|
||||
|
||||
def purchase(money, credit_card_or_reference, options = {})
|
||||
post = {}
|
||||
|
||||
add_amount(post, money, options)
|
||||
add_creditcard_or_reference(post, credit_card_or_reference)
|
||||
add_invoice(post, options)
|
||||
add_instant_capture(post, true)
|
||||
|
||||
commit(:authorize, post)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
post = {}
|
||||
|
||||
add_reference(post, authorization)
|
||||
add_amount_without_currency(post, money)
|
||||
|
||||
commit(:capture, post)
|
||||
end
|
||||
|
||||
def void(identification, options = {})
|
||||
post = {}
|
||||
|
||||
add_reference(post, identification)
|
||||
|
||||
commit(:void, post)
|
||||
end
|
||||
|
||||
def refund(money, identification, options = {})
|
||||
post = {}
|
||||
|
||||
add_amount_without_currency(post, money)
|
||||
add_reference(post, identification)
|
||||
|
||||
commit(:credit, post)
|
||||
end
|
||||
|
||||
def credit(money, identification, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, identification, options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_amount(post, money, options)
|
||||
post[:amount] = amount(money)
|
||||
post[:currency] = CURRENCY_CODES[(options[:currency] || currency(money)).to_sym]
|
||||
end
|
||||
|
||||
def add_amount_without_currency(post, money)
|
||||
post[:amount] = amount(money)
|
||||
end
|
||||
|
||||
def add_reference(post, identification)
|
||||
post[:transaction] = identification
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:orderid] = format_order_number(options[:order_id])
|
||||
end
|
||||
|
||||
def add_creditcard(post, credit_card)
|
||||
post[:cardno] = credit_card.number
|
||||
post[:cvc] = credit_card.verification_value
|
||||
post[:expmonth] = credit_card.month
|
||||
post[:expyear] = credit_card.year
|
||||
end
|
||||
|
||||
def add_creditcard_or_reference(post, credit_card_or_reference)
|
||||
if credit_card_or_reference.respond_to?(:number)
|
||||
add_creditcard(post, credit_card_or_reference)
|
||||
else
|
||||
add_reference(post, credit_card_or_reference.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def add_instant_capture(post, option)
|
||||
post[:instantcapture] = option ? 1 : 0
|
||||
end
|
||||
|
||||
def commit(action, params)
|
||||
response = send("do_#{action}", params)
|
||||
|
||||
if action == :authorize
|
||||
Response.new response['accept'].to_i == 1,
|
||||
response['errortext'],
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => response['tid']
|
||||
else
|
||||
Response.new response['result'] == 'true',
|
||||
messages(response['epay'], response['pbs']),
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => params[:transaction]
|
||||
end
|
||||
end
|
||||
|
||||
def messages(epay, pbs = nil)
|
||||
response = "ePay: #{epay}"
|
||||
response << " PBS: #{pbs}" if pbs
|
||||
return response
|
||||
end
|
||||
|
||||
def soap_post(method, params)
|
||||
data = xml_builder(params, method)
|
||||
headers = make_headers(data, method)
|
||||
REXML::Document.new(ssl_post('https://' + API_HOST + '/remote/payment.asmx', data, headers))
|
||||
end
|
||||
|
||||
def do_authorize(params)
|
||||
headers = {}
|
||||
headers['Referer'] = (options[:password] || "activemerchant.org")
|
||||
|
||||
response = raw_ssl_request(:post, 'https://' + API_HOST + '/auth/default.aspx', authorize_post_data(params), headers)
|
||||
|
||||
# Authorize gives the response back by redirecting with the values in
|
||||
# the URL query
|
||||
if location = response['Location']
|
||||
query = CGI::parse(URI.parse(location.gsub(' ', '%20')).query)
|
||||
else
|
||||
return {
|
||||
'accept' => '0',
|
||||
'errortext' => 'ePay did not respond as expected. Please try again.',
|
||||
'response_code' => response.code,
|
||||
'response_message' => response.message
|
||||
}
|
||||
end
|
||||
|
||||
result = {}
|
||||
query.each_pair do |k,v|
|
||||
result[k] = v.is_a?(Array) && v.size == 1 ? v[0] : v # make values like ['v'] into 'v'
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def do_capture(params)
|
||||
response = soap_post('capture', params)
|
||||
{
|
||||
'result' => response.elements['//captureResponse/captureResult'].text,
|
||||
'pbs' => response.elements['//captureResponse/pbsResponse'].text,
|
||||
'epay' => response.elements['//captureResponse/epayresponse'].text
|
||||
}
|
||||
end
|
||||
|
||||
def do_credit(params)
|
||||
response = soap_post('credit', params)
|
||||
{
|
||||
'result' => response.elements['//creditResponse/creditResult'].text,
|
||||
'pbs' => response.elements['//creditResponse/pbsresponse'].text,
|
||||
'epay' => response.elements['//creditResponse/epayresponse'].text
|
||||
}
|
||||
end
|
||||
|
||||
def do_void(params)
|
||||
response = soap_post('delete', params)
|
||||
{
|
||||
'result' => response.elements['//deleteResponse/deleteResult'].text,
|
||||
'epay' => response.elements['//deleteResponse/epayresponse'].text
|
||||
}
|
||||
end
|
||||
|
||||
def make_headers(data, soap_call)
|
||||
{
|
||||
'Content-Type' => 'text/xml; charset=utf-8',
|
||||
'Host' => API_HOST,
|
||||
'Content-Length' => data.size.to_s,
|
||||
'SOAPAction' => self.live_url + '/' + soap_call
|
||||
}
|
||||
end
|
||||
|
||||
def xml_builder(params, soap_call)
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!
|
||||
xml.tag! 'soap:Envelope', { 'xmlns:xsi' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
||||
'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' } do
|
||||
xml.tag! 'soap:Body' do
|
||||
xml.tag! soap_call, { 'xmlns' => self.live_url } do
|
||||
xml.tag! 'merchantnumber', @options[:login]
|
||||
xml.tag! 'transactionid', params[:transaction]
|
||||
xml.tag! 'amount', params[:amount].to_s if soap_call != 'delete'
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def authorize_post_data(params = {})
|
||||
params[:language] = '2'
|
||||
params[:cms] = 'activemerchant'
|
||||
params[:accepturl] = 'https://ssl.ditonlinebetalingssystem.dk/auth/default.aspx?accept=1'
|
||||
params[:declineurl] = 'https://ssl.ditonlinebetalingssystem.dk/auth/default.aspx?decline=1'
|
||||
params[:merchantnumber] = @options[:login]
|
||||
|
||||
params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
# Limited to 20 digits max
|
||||
def format_order_number(number)
|
||||
number.to_s.gsub(/[^\w_]/, '').rjust(4, "0")[0...20]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
308
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/evo_ca.rb
vendored
Normal file
308
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/evo_ca.rb
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# === EVO Canada payment gateway.
|
||||
#
|
||||
# EVO returns two different identifiers for most transactions, the
|
||||
# +authcode+ and the +transactionid+. Since +transactionid+ is used more
|
||||
# often (i.e. for {#capture}, {#refund}, {#void} and {#update}) we store it in the
|
||||
# Response#authorization attribute. The +authcode+ from the merchant
|
||||
# account is accessible via {Response#params}.
|
||||
#
|
||||
# Two different but related response messages are also returned from EVO.
|
||||
# The message indicated by EVO's <tt>response_code</tt> parameter is returned as
|
||||
# {Response#message} (Those messages can be seen in the {MESSAGES} hash.)
|
||||
# The other, shorter message is available via {Response#params}.
|
||||
#
|
||||
# It's recommended to save the contents of the {Response#params} in your
|
||||
# transaction log for future reference.
|
||||
#
|
||||
# === Sample Use
|
||||
#
|
||||
# gateway = ActiveMerchant::Billing::EvoCaGateway.new(username: 'demo', password: 'password')
|
||||
#
|
||||
# response = gateway.authorize(1000, credit_card, options)
|
||||
#
|
||||
# puts response.authorization # the transactionid
|
||||
# puts response.params['authcode'] # the authcode from the merchant account
|
||||
# puts response.message # the 'pretty' response message
|
||||
# puts response.params['responsetext'] # the 'terse' response message
|
||||
#
|
||||
# gateway.capture(1000, response.authorization)
|
||||
# gateway.update(response.authorization, shipping_carrier: 'fedex')
|
||||
# gateway.refund(500, response.authorization)
|
||||
#
|
||||
class EvoCaGateway < Gateway
|
||||
self.test_url = 'https://secure.evoepay.com/api/transact.php'
|
||||
self.live_url = 'https://secure.evoepay.com/api/transact.php'
|
||||
|
||||
self.supported_countries = ['CA']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover]
|
||||
self.money_format = :dollars
|
||||
self.homepage_url = 'http://www.evocanada.com/'
|
||||
self.display_name = 'EVO Canada'
|
||||
|
||||
APPROVED, DECLINED, ERROR = 1, 2, 3
|
||||
|
||||
MESSAGES = {
|
||||
100 => 'Transaction was approved',
|
||||
200 => 'Transaction was declined by processor',
|
||||
201 => 'Do not honor',
|
||||
202 => 'Insufficient funds',
|
||||
203 => 'Over limit',
|
||||
204 => 'Transaction not allowed',
|
||||
220 => 'Incorrect payment data',
|
||||
221 => 'No such card issuer',
|
||||
222 => 'No card number on file with issuer',
|
||||
223 => 'Expired card',
|
||||
224 => 'Invalid expiration date',
|
||||
225 => 'Invalid card security code',
|
||||
240 => 'Call issuer for futher information',
|
||||
250 => 'Pick up card',
|
||||
251 => 'Lost card',
|
||||
252 => 'Stolen card',
|
||||
253 => 'Fraudulant card',
|
||||
260 => 'Declined with further instructions available',
|
||||
261 => 'Declined - stop all recurring payments',
|
||||
262 => 'Declined - stop this recurring program',
|
||||
263 => 'Declined - updated cardholder data available',
|
||||
264 => 'Declined - retry in a few days',
|
||||
300 => 'Transaction was rejected by gateway',
|
||||
400 => 'Transaction error returned by processor',
|
||||
410 => 'Invalid merchant configuration',
|
||||
411 => 'Merchant account is inactive',
|
||||
420 => 'Communication error',
|
||||
421 => 'Communication error with issuer',
|
||||
430 => 'Duplicate transaction at processor',
|
||||
440 => 'Processor format error',
|
||||
441 => 'Invalid transaction information',
|
||||
460 => 'Processor feature not available',
|
||||
461 => 'Unsupported card type'
|
||||
}
|
||||
|
||||
# This gateway requires that a valid username and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# === Required Options
|
||||
#
|
||||
# * <tt>:username</tt>
|
||||
# * <tt>:password</tt>
|
||||
def initialize(options = {})
|
||||
requires!(options, :username, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Transaction sales are submitted and immediately flagged for settlement.
|
||||
# These transactions will automatically be settled.
|
||||
#
|
||||
# Payment source can be either a {CreditCard} or {Check}.
|
||||
#
|
||||
# === Additional Options
|
||||
# In addition to the standard options, this gateway supports
|
||||
#
|
||||
# * <tt>:tracking_number</tt> - Shipping tracking number
|
||||
# * <tt>:shipping_carrier</tt> - ups/fedex/dhl/usps
|
||||
# * <tt>:po_number</tt> - Purchase order
|
||||
# * <tt>:tax</tt> - Tax amount
|
||||
# * <tt>:shipping</tt> - Shipping cost
|
||||
def purchase(money, credit_card_or_check, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_order(post, options)
|
||||
add_paymentmethod(post, credit_card_or_check)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('sale', money, post)
|
||||
end
|
||||
|
||||
# Transaction authorizations are authorized immediately but are not
|
||||
# flagged for settlement. These transactions must be flagged for
|
||||
# settlement using the _capture_ transaction type. Authorizations
|
||||
# typically remain activate for three to seven business days.
|
||||
#
|
||||
# Payment source must be a {CreditCard}.
|
||||
def authorize(money, credit_card, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_order(post, options)
|
||||
add_paymentmethod(post, credit_card)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('auth', money, post)
|
||||
end
|
||||
|
||||
# Transaction captures flag existing _authorizations_ for settlement. Only
|
||||
# authorizations can be captured. Captures can be submitted for an amount
|
||||
# equal to or less than the original authorization.
|
||||
#
|
||||
# The <tt>authorization</tt> parameter is the transaction ID, retrieved
|
||||
# from Response#authorization. See EvoCaGateway#purchase for the
|
||||
# <tt>options</tt>.
|
||||
def capture(money, authorization, options = {})
|
||||
post = {
|
||||
:amount => amount(money),
|
||||
:transactionid => authorization
|
||||
}
|
||||
add_order(post, options)
|
||||
commit('capture', money, post)
|
||||
end
|
||||
|
||||
# Transaction refunds will reverse a previously settled transaction. If
|
||||
# the transaction has not been settled, it must be _voided_ instead of
|
||||
# refunded.
|
||||
#
|
||||
# The <tt>identification</tt> parameter is the transaction ID, retrieved
|
||||
# from {Response#authorization}.
|
||||
def refund(money, identification)
|
||||
post = {:transactionid => identification}
|
||||
commit('refund', money, post)
|
||||
end
|
||||
|
||||
# Transaction credits apply a negative amount to the cardholder's card.
|
||||
# In most situations credits are disabled as transaction refunds should
|
||||
# be used instead.
|
||||
#
|
||||
# Note that this is different from a {#refund} (which is usually what
|
||||
# you'll be looking for).
|
||||
def credit(money, credit_card, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_order(post, options)
|
||||
add_paymentmethod(post, credit_card)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('credit', money, post)
|
||||
end
|
||||
|
||||
# Transaction voids will cancel an existing sale or captured
|
||||
# authorization. In addition, non-captured authorizations can be voided to
|
||||
# prevent any future capture. Voids can only occur if the transaction has
|
||||
# not been settled.
|
||||
#
|
||||
# The <tt>identification</tt> parameter is the transaction ID, retrieved
|
||||
# from {Response#authorization}.
|
||||
def void(identification)
|
||||
post = {:transactionid => identification}
|
||||
commit('void', nil, post)
|
||||
end
|
||||
|
||||
# Transaction updates can be used to update previous transactions with
|
||||
# specific order information, such as a tracking number and shipping
|
||||
# carrier. See EvoCaGateway#purchase for <tt>options</tt>.
|
||||
#
|
||||
# The <tt>identification</tt> parameter is the transaction ID, retrieved
|
||||
# from {Response#authorization}.
|
||||
def update(identification, options)
|
||||
post = {:transactionid => identification}
|
||||
add_order(post, options)
|
||||
commit('update', nil, post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:email] = options[:email]
|
||||
post[:ipaddress] = options[:ip]
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:firstname] = address[:first_name]
|
||||
post[:lastname] = address[:last_name]
|
||||
post[:address1] = address[:address1]
|
||||
post[:address2] = address[:address2]
|
||||
post[:company] = address[:company]
|
||||
post[:phone] = address[:phone]
|
||||
post[:city] = address[:city]
|
||||
post[:state] = address[:state]
|
||||
post[:zip] = address[:zip]
|
||||
post[:country] = address[:country]
|
||||
end
|
||||
|
||||
if address = options[:shipping_address]
|
||||
post[:shipping_firstname] = address[:first_name]
|
||||
post[:shipping_lastname] = address[:last_name]
|
||||
post[:shipping_address1] = address[:address1]
|
||||
post[:shipping_address2] = address[:address2]
|
||||
post[:shipping_company] = address[:company]
|
||||
post[:shipping_zip] = address[:zip]
|
||||
post[:shipping_city] = address[:city]
|
||||
post[:shipping_state] = address[:state]
|
||||
post[:shipping_country] = address[:country]
|
||||
end
|
||||
end
|
||||
|
||||
def add_order(post, options)
|
||||
post[:orderid] = options[:order_id]
|
||||
post[:tracking_number] = options[:tracking_number]
|
||||
post[:shipping_carrier] = options[:shipping_carrier]
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:orderdescription] = options[:description]
|
||||
post[:ponumber] = options[:po_number]
|
||||
post[:shipping] = amount(options[:shipping])
|
||||
post[:tax] = amount(options[:tax])
|
||||
end
|
||||
|
||||
def add_paymentmethod(post, payment)
|
||||
if card_brand(payment)=='check'
|
||||
post[:payment] = 'check'
|
||||
post[:checkname] = payment.name
|
||||
post[:checkaba] = payment.routing_number
|
||||
post[:checkaccount] = payment.account_number
|
||||
post[:account_holder_type] = payment.account_holder_type
|
||||
post[:account_type] = payment.account_type
|
||||
else
|
||||
post[:payment] = 'creditcard'
|
||||
post[:ccnumber] = payment.number
|
||||
post[:ccexp] = "#{format(payment.month, :two_digits)}#{format(payment.year, :two_digits)}"
|
||||
post[:cvv] = payment.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
fields = {}
|
||||
CGI::parse(body).each do |k, v|
|
||||
fields[k.to_s] = v.kind_of?(Array) ? v[0] : v
|
||||
end
|
||||
fields
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response['response'].to_i == APPROVED
|
||||
end
|
||||
|
||||
def commit(action, money, parameters)
|
||||
parameters[:amount] = amount(money) unless action == 'void'
|
||||
|
||||
data = ssl_post self.live_url, post_data(action, parameters)
|
||||
response = parse(data)
|
||||
message = message_from(response)
|
||||
|
||||
Response.new(success?(response), message, response,
|
||||
:test => test?,
|
||||
:authorization => response['transactionid'],
|
||||
:avs_result => { :code => response['avsresponse'] },
|
||||
:cvv_result => response['cvvresponse']
|
||||
)
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
MESSAGES.fetch(response['response_code'].to_i, false) || response['message']
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {:type => action}
|
||||
|
||||
if test?
|
||||
post[:username] = 'demo'
|
||||
post[:password] = 'password'
|
||||
else
|
||||
post[:username] = options[:username]
|
||||
post[:password] = options[:password]
|
||||
end
|
||||
post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" unless value.nil? }.compact.join("&")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
225
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway.rb
vendored
Normal file
225
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway.rb
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
require 'rexml/document'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# Public: For more information on the Eway Gateway please visit their
|
||||
# {Developers Area}[http://www.eway.com.au/developers/api/direct-payments]
|
||||
class EwayGateway < Gateway
|
||||
self.live_url = 'https://www.eway.com.au'
|
||||
|
||||
self.money_format = :cents
|
||||
self.supported_countries = ['AU']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
|
||||
self.homepage_url = 'http://www.eway.com.au/'
|
||||
self.display_name = 'eWAY'
|
||||
|
||||
# Public: Create a new Eway Gateway.
|
||||
# options - A hash of options:
|
||||
# :login - Your Customer ID.
|
||||
# :password - Your XML Refund Password that you
|
||||
# specified on the Eway site. (optional)
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
requires_address!(options)
|
||||
|
||||
post = {}
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_id(post)
|
||||
add_invoice_data(post, options)
|
||||
add_non_optional_data(post)
|
||||
add_amount(post, money)
|
||||
post[:CustomerEmail] = options[:email]
|
||||
|
||||
commit(purchase_url(post[:CVN]), money, post)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options={})
|
||||
post = {}
|
||||
|
||||
add_customer_id(post)
|
||||
add_amount(post, money)
|
||||
add_non_optional_data(post)
|
||||
post[:OriginalTrxnNumber] = authorization
|
||||
post[:RefundPassword] = @options[:password]
|
||||
post[:CardExpiryMonth] = nil
|
||||
post[:CardExpiryYear] = nil
|
||||
|
||||
commit(refund_url, money, post)
|
||||
end
|
||||
|
||||
private
|
||||
def requires_address!(options)
|
||||
raise ArgumentError.new("Missing eWay required parameters: address or billing_address") unless (options.has_key?(:address) or options.has_key?(:billing_address))
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:CardNumber] = creditcard.number
|
||||
post[:CardExpiryMonth] = sprintf("%.2i", creditcard.month)
|
||||
post[:CardExpiryYear] = sprintf("%.4i", creditcard.year)[-2..-1]
|
||||
post[:CustomerFirstName] = creditcard.first_name
|
||||
post[:CustomerLastName] = creditcard.last_name
|
||||
post[:CardHoldersName] = creditcard.name
|
||||
|
||||
post[:CVN] = creditcard.verification_value if creditcard.verification_value?
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:CustomerAddress] = [ address[:address1], address[:address2], address[:city], address[:state], address[:country] ].compact.join(', ')
|
||||
post[:CustomerPostcode] = address[:zip]
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_id(post)
|
||||
post[:CustomerID] = @options[:login]
|
||||
end
|
||||
|
||||
def add_invoice_data(post, options)
|
||||
post[:CustomerInvoiceRef] = options[:order_id]
|
||||
post[:CustomerInvoiceDescription] = options[:description]
|
||||
end
|
||||
|
||||
def add_amount(post, money)
|
||||
post[:TotalAmount] = amount(money)
|
||||
end
|
||||
|
||||
def add_non_optional_data(post)
|
||||
post[:Option1] = nil
|
||||
post[:Option2] = nil
|
||||
post[:Option3] = nil
|
||||
post[:TrxnNumber] = nil
|
||||
end
|
||||
|
||||
def commit(url, money, parameters)
|
||||
raw_response = ssl_post(url, post_data(parameters))
|
||||
response = parse(raw_response)
|
||||
|
||||
Response.new(success?(response),
|
||||
message_from(response[:ewaytrxnerror]),
|
||||
response,
|
||||
:authorization => response[:ewaytrxnnumber],
|
||||
:test => test?
|
||||
)
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response[:ewaytrxnstatus] == "True"
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
xml.elements.each('//ewayResponse/*') do |node|
|
||||
response[node.name.downcase.to_sym] = normalize(node.text)
|
||||
end unless xml.root.nil?
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def post_data(parameters = {})
|
||||
xml = REXML::Document.new
|
||||
root = xml.add_element("ewaygateway")
|
||||
|
||||
parameters.each do |key, value|
|
||||
root.add_element("eway#{key}").text = value
|
||||
end
|
||||
xml.to_s
|
||||
end
|
||||
|
||||
def message_from(message)
|
||||
return '' if message.blank?
|
||||
MESSAGES[message[0,2]] || message
|
||||
end
|
||||
|
||||
# Make a ruby type out of the response string
|
||||
def normalize(field)
|
||||
case field
|
||||
when "true" then true
|
||||
when "false" then false
|
||||
when "" then nil
|
||||
when "null" then nil
|
||||
else field
|
||||
end
|
||||
end
|
||||
|
||||
def purchase_url(cvn)
|
||||
suffix = test? ? 'xmltest/testpage.asp' : 'xmlpayment.asp'
|
||||
gateway_part = cvn ? 'gateway_cvn' : 'gateway'
|
||||
"#{live_url}/#{gateway_part}/#{suffix}"
|
||||
end
|
||||
|
||||
def refund_url
|
||||
suffix = test? ? 'xmltest/refund_test.asp' : 'xmlpaymentrefund.asp'
|
||||
"#{live_url}/gateway/#{suffix}"
|
||||
end
|
||||
|
||||
MESSAGES = {
|
||||
"00" => "Transaction Approved",
|
||||
"01" => "Refer to Issuer",
|
||||
"02" => "Refer to Issuer, special",
|
||||
"03" => "No Merchant",
|
||||
"04" => "Pick Up Card",
|
||||
"05" => "Do Not Honour",
|
||||
"06" => "Error",
|
||||
"07" => "Pick Up Card, Special",
|
||||
"08" => "Honour With Identification",
|
||||
"09" => "Request In Progress",
|
||||
"10" => "Approved For Partial Amount",
|
||||
"11" => "Approved, VIP",
|
||||
"12" => "Invalid Transaction",
|
||||
"13" => "Invalid Amount",
|
||||
"14" => "Invalid Card Number",
|
||||
"15" => "No Issuer",
|
||||
"16" => "Approved, Update Track 3",
|
||||
"19" => "Re-enter Last Transaction",
|
||||
"21" => "No Action Taken",
|
||||
"22" => "Suspected Malfunction",
|
||||
"23" => "Unacceptable Transaction Fee",
|
||||
"25" => "Unable to Locate Record On File",
|
||||
"30" => "Format Error",
|
||||
"31" => "Bank Not Supported By Switch",
|
||||
"33" => "Expired Card, Capture",
|
||||
"34" => "Suspected Fraud, Retain Card",
|
||||
"35" => "Card Acceptor, Contact Acquirer, Retain Card",
|
||||
"36" => "Restricted Card, Retain Card",
|
||||
"37" => "Contact Acquirer Security Department, Retain Card",
|
||||
"38" => "PIN Tries Exceeded, Capture",
|
||||
"39" => "No Credit Account",
|
||||
"40" => "Function Not Supported",
|
||||
"41" => "Lost Card",
|
||||
"42" => "No Universal Account",
|
||||
"43" => "Stolen Card",
|
||||
"44" => "No Investment Account",
|
||||
"51" => "Insufficient Funds",
|
||||
"52" => "No Cheque Account",
|
||||
"53" => "No Savings Account",
|
||||
"54" => "Expired Card",
|
||||
"55" => "Incorrect PIN",
|
||||
"56" => "No Card Record",
|
||||
"57" => "Function Not Permitted to Cardholder",
|
||||
"58" => "Function Not Permitted to Terminal",
|
||||
"59" => "Suspected Fraud",
|
||||
"60" => "Acceptor Contact Acquirer",
|
||||
"61" => "Exceeds Withdrawal Limit",
|
||||
"62" => "Restricted Card",
|
||||
"63" => "Security Violation",
|
||||
"64" => "Original Amount Incorrect",
|
||||
"66" => "Acceptor Contact Acquirer, Security",
|
||||
"67" => "Capture Card",
|
||||
"75" => "PIN Tries Exceeded",
|
||||
"82" => "CVV Validation Error",
|
||||
"90" => "Cutoff In Progress",
|
||||
"91" => "Card Issuer Unavailable",
|
||||
"92" => "Unable To Route Transaction",
|
||||
"93" => "Cannot Complete, Violation Of The Law",
|
||||
"94" => "Duplicate Transaction",
|
||||
"96" => "System Error"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
291
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway_managed.rb
vendored
Normal file
291
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway_managed.rb
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class EwayManagedGateway < Gateway
|
||||
self.test_url = 'https://www.eway.com.au/gateway/ManagedPaymentService/test/managedCreditCardPayment.asmx'
|
||||
self.live_url = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['AU']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master]
|
||||
|
||||
self.default_currency = 'AUD'
|
||||
|
||||
#accepted money format
|
||||
self.money_format = :cents
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.eway.com.au/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'eWay Managed Payments'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :username, :password)
|
||||
|
||||
# eWay returns 500 code for faults, which AM snaffles.
|
||||
# So, we tell it to allow them.
|
||||
options[:ignore_http_status] = true
|
||||
super
|
||||
end
|
||||
|
||||
# add a new customer CC to your eway account and return unique ManagedCustomerID
|
||||
# supports storing details required by eway see "add_creditcard" and "add_address"
|
||||
def store(creditcard, options = {})
|
||||
post = {}
|
||||
|
||||
# Handle our required fields
|
||||
requires!(options, :billing_address)
|
||||
|
||||
# Handle eWay specific required fields.
|
||||
billing_address = options[:billing_address]
|
||||
eway_requires!(billing_address)
|
||||
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, billing_address)
|
||||
add_misc_fields(post, options)
|
||||
|
||||
commit("CreateCustomer", post)
|
||||
end
|
||||
|
||||
def update(billing_id, creditcard, options={})
|
||||
post = {}
|
||||
|
||||
# Handle our required fields
|
||||
requires!(options, :billing_address)
|
||||
|
||||
# Handle eWay specific required fields.
|
||||
billing_address = options[:billing_address]
|
||||
eway_requires!(billing_address)
|
||||
|
||||
post[:managedCustomerID]=billing_id
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, billing_address)
|
||||
add_misc_fields(post, options)
|
||||
|
||||
commit("UpdateCustomer", post)
|
||||
end
|
||||
|
||||
# Process a payment in the given amount against the stored credit card given by billing_id
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
||||
# * <tt>billing_id</tt> -- The eWay provided card/customer token to charge (managedCustomerID)
|
||||
# * <tt>options</tt> -- A hash of optional parameters.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:order_id</tt> -- The order number, passed to eWay as the "Invoice Reference"
|
||||
# * <tt>:invoice</tt> -- The invoice number, passed to eWay as the "Invoice Reference" unless :order_id is also given
|
||||
# * <tt>:description</tt> -- A description of the payment, passed to eWay as the "Invoice Description"
|
||||
def purchase(money, billing_id, options={})
|
||||
post = {}
|
||||
post[:managedCustomerID] = billing_id.to_s
|
||||
post[:amount]=money
|
||||
add_invoice(post, options)
|
||||
|
||||
commit("ProcessPayment", post)
|
||||
end
|
||||
|
||||
# Get customer's stored credit card details given by billing_id
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>billing_id</tt> -- The eWay provided card/customer token to charge (managedCustomerID)
|
||||
def retrieve(billing_id)
|
||||
post = {}
|
||||
post[:managedCustomerID] = billing_id.to_s
|
||||
|
||||
commit("QueryCustomer", post)
|
||||
end
|
||||
|
||||
# TODO: eWay API also provides QueryPayment
|
||||
|
||||
private
|
||||
|
||||
def eway_requires!(hash)
|
||||
raise ArgumentError.new("Missing eWay required parameter in `billing_address`: title") unless hash.has_key?(:title)
|
||||
raise ArgumentError.new("Missing eWay required parameter in `billing_address`: country") unless hash.has_key?(:country)
|
||||
end
|
||||
|
||||
def add_address(post, address)
|
||||
post[:Address] = address[:address1].to_s
|
||||
post[:Phone] = address[:phone].to_s
|
||||
post[:PostCode] = address[:zip].to_s
|
||||
post[:Suburb] = address[:city].to_s
|
||||
post[:Country] = address[:country].to_s.downcase
|
||||
post[:State] = address[:state].to_s
|
||||
post[:Mobile] = address[:mobile].to_s
|
||||
post[:Fax] = address[:fax].to_s
|
||||
end
|
||||
|
||||
def add_misc_fields(post, options)
|
||||
post[:CustomerRef]=options[:billing_address][:customer_ref] || options[:customer]
|
||||
post[:Title]=options[:billing_address][:title]
|
||||
post[:Company]=options[:billing_address][:company]
|
||||
post[:JobDesc]=options[:billing_address][:job_desc]
|
||||
post[:Email]=options[:billing_address][:email] || options[:email]
|
||||
post[:URL]=options[:billing_address][:url]
|
||||
post[:Comments]=options[:description]
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:invoiceReference] = options[:order_id] || options[:invoice]
|
||||
post[:invoiceDescription] = options[:description]
|
||||
end
|
||||
|
||||
|
||||
# add credit card details to be stored by eway. NOTE eway requires "title" field
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:CCNumber] = creditcard.number
|
||||
post[:CCExpiryMonth] = sprintf("%.2i", creditcard.month)
|
||||
post[:CCExpiryYear] = sprintf("%.4i", creditcard.year)[-2..-1]
|
||||
post[:CCNameOnCard] = creditcard.name
|
||||
post[:FirstName] = creditcard.first_name
|
||||
post[:LastName] = creditcard.last_name
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
reply = {}
|
||||
xml = REXML::Document.new(body)
|
||||
if root = REXML::XPath.first(xml, "//soap:Fault") then
|
||||
reply=parse_fault(root)
|
||||
else
|
||||
if root = REXML::XPath.first(xml, '//ProcessPaymentResponse/ewayResponse') then
|
||||
# Successful payment
|
||||
reply=parse_purchase(root)
|
||||
else
|
||||
if root = REXML::XPath.first(xml, '//QueryCustomerResult') then
|
||||
reply=parse_query_customer(root)
|
||||
else
|
||||
if root = REXML::XPath.first(xml, '//CreateCustomerResult') then
|
||||
reply[:message]='OK'
|
||||
reply[:CreateCustomerResult]=root.text
|
||||
reply[:success]=true
|
||||
else
|
||||
if root = REXML::XPath.first(xml, '//UpdateCustomerResult') then
|
||||
if root.text.downcase == 'true' then
|
||||
reply[:message]='OK'
|
||||
reply[:success]=true
|
||||
else
|
||||
# ERROR: This state should never occur. If there is a problem,
|
||||
# a soap:Fault will be returned. The presence of this
|
||||
# element always means a success.
|
||||
raise StandardError, "Unexpected \"false\" in UpdateCustomerResult"
|
||||
end
|
||||
else
|
||||
# ERROR: This state should never occur currently. We have handled
|
||||
# responses for all the methods which we support.
|
||||
raise StandardError, "Unexpected response"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return reply
|
||||
end
|
||||
|
||||
def parse_fault(node)
|
||||
reply={}
|
||||
reply[:message]=REXML::XPath.first(node, '//soap:Reason/soap:Text').text
|
||||
reply[:success]=false
|
||||
reply
|
||||
end
|
||||
|
||||
def parse_purchase(node)
|
||||
reply={}
|
||||
reply[:message]=REXML::XPath.first(node, '//ewayTrxnError').text
|
||||
reply[:success]=(REXML::XPath.first(node, '//ewayTrxnStatus').text == 'True')
|
||||
reply[:auth_code]=REXML::XPath.first(node, '//ewayAuthCode').text
|
||||
reply[:transaction_number]=REXML::XPath.first(node, '//ewayTrxnNumber').text
|
||||
reply
|
||||
end
|
||||
|
||||
def parse_query_customer(node)
|
||||
reply={}
|
||||
reply[:message]='OK'
|
||||
reply[:success]=true
|
||||
reply[:CCNumber]=REXML::XPath.first(node, '//CCNumber').text
|
||||
reply[:CCName]=REXML::XPath.first(node, '//CCName').text
|
||||
reply[:CCExpiryMonth]=REXML::XPath.first(node, '//CCExpiryMonth').text
|
||||
reply[:CCExpiryYear]=REXML::XPath.first(node, '//CCExpiryYear').text
|
||||
reply
|
||||
end
|
||||
|
||||
def commit(action, post)
|
||||
raw = begin
|
||||
ssl_post(test? ? self.test_url : self.live_url, soap_request(post, action), 'Content-Type' => 'application/soap+xml; charset=utf-8')
|
||||
rescue ResponseError => e
|
||||
e.response.body
|
||||
end
|
||||
response = parse(raw)
|
||||
|
||||
EwayResponse.new(response[:success], response[:message], response,
|
||||
:test => test?,
|
||||
:authorization => response[:auth_code]
|
||||
)
|
||||
end
|
||||
|
||||
# Where we build the full SOAP 1.2 request using builder
|
||||
def soap_request(arguments, action)
|
||||
# eWay demands all fields be sent, but contain an empty string if blank
|
||||
post = case action
|
||||
when 'QueryCustomer'
|
||||
arguments
|
||||
when 'ProcessPayment'
|
||||
default_payment_fields.merge(arguments)
|
||||
when 'CreateCustomer'
|
||||
default_customer_fields.merge(arguments)
|
||||
when 'UpdateCustomer'
|
||||
default_customer_fields.merge(arguments)
|
||||
end
|
||||
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! 'soap12:Envelope', {'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'} do
|
||||
xml.tag! 'soap12:Header' do
|
||||
xml.tag! 'eWAYHeader', {'xmlns' => 'https://www.eway.com.au/gateway/managedpayment'} do
|
||||
xml.tag! 'eWAYCustomerID', @options[:login]
|
||||
xml.tag! 'Username', @options[:username]
|
||||
xml.tag! 'Password', @options[:password]
|
||||
end
|
||||
end
|
||||
xml.tag! 'soap12:Body' do |x|
|
||||
x.tag! "#{action}", {'xmlns' => 'https://www.eway.com.au/gateway/managedpayment'} do |y|
|
||||
post.each do |key, value|
|
||||
y.tag! "#{key}", "#{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def default_customer_fields
|
||||
hash={}
|
||||
%w( CustomerRef Title FirstName LastName Company JobDesc Email Address Suburb State PostCode Country Phone Mobile Fax URL Comments CCNumber CCNameOnCard CCExpiryMonth CCExpiryYear ).each do |field|
|
||||
hash[field.to_sym]=''
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
def default_payment_fields
|
||||
hash={}
|
||||
%w( managedCustomerID amount invoiceReference invoiceDescription ).each do |field|
|
||||
hash[field.to_sym]=''
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
class EwayResponse < Response
|
||||
# add a method to response so we can easily get the eway token "ManagedCustomerID"
|
||||
def token
|
||||
@params['CreateCustomerResult']
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
300
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway_rapid.rb
vendored
Normal file
300
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/eway_rapid.rb
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
require "nokogiri"
|
||||
require "cgi"
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class EwayRapidGateway < Gateway
|
||||
self.test_url = "https://api.sandbox.ewaypayments.com/"
|
||||
self.live_url = "https://api.ewaypayments.com/"
|
||||
|
||||
self.money_format = :cents
|
||||
self.supported_countries = ["AU"]
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
|
||||
self.homepage_url = "http://www.eway.com.au/"
|
||||
self.display_name = "eWAY Rapid 3.0"
|
||||
self.default_currency = "AUD"
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Public: Run a purchase transaction. Treats the Rapid 3.0 transparent
|
||||
# redirect as an API endpoint in order to conform to the standard
|
||||
# ActiveMerchant #purchase API.
|
||||
#
|
||||
# amount - The monetary amount of the transaction in cents.
|
||||
# options - A standard ActiveMerchant options hash:
|
||||
# :order_id - A merchant-supplied identifier for the
|
||||
# transaction (optional).
|
||||
# :description - A merchant-supplied description of the
|
||||
# transaction (optional).
|
||||
# :currency - Three letter currency code for the
|
||||
# transaction (default: "AUD")
|
||||
# :billing_address - Standard ActiveMerchant address hash
|
||||
# (optional).
|
||||
# :shipping_address - Standard ActiveMerchant address hash
|
||||
# (optional).
|
||||
# :ip - The ip of the consumer initiating the
|
||||
# transaction (optional).
|
||||
# :application_id - A string identifying the application
|
||||
# submitting the transaction
|
||||
# (default: "https://github.com/Shopify/active_merchant")
|
||||
#
|
||||
# Returns an ActiveMerchant::Billing::Response object
|
||||
def purchase(amount, payment_method, options={})
|
||||
MultiResponse.new.tap do |r|
|
||||
# Rather than follow the redirect, we detect the 302 and capture the
|
||||
# token out of the Location header in the run_purchase step. But we
|
||||
# still need a placeholder url to pass to eWay, and that is what
|
||||
# example.com is used for here.
|
||||
r.process{setup_purchase(amount, options.merge(:redirect_url => "http://example.com/"))}
|
||||
r.process{run_purchase(r.authorization, payment_method, r.params["formactionurl"])}
|
||||
r.process{status(r.authorization)}
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Acquire the token necessary to run a transparent redirect.
|
||||
#
|
||||
# amount - The monetary amount of the transaction in cents.
|
||||
# options - A supplemented ActiveMerchant options hash:
|
||||
# :redirect_url - The url to return the customer to after
|
||||
# the transparent redirect is completed
|
||||
# (required).
|
||||
# :order_id - A merchant-supplied identifier for the
|
||||
# transaction (optional).
|
||||
# :description - A merchant-supplied description of the
|
||||
# transaction (optional).
|
||||
# :currency - Three letter currency code for the
|
||||
# transaction (default: "AUD")
|
||||
# :billing_address - Standard ActiveMerchant address hash
|
||||
# (optional).
|
||||
# :shipping_address - Standard ActiveMerchant address hash
|
||||
# (optional).
|
||||
# :ip - The ip of the consumer initiating the
|
||||
# transaction (optional).
|
||||
# :application_id - A string identifying the application
|
||||
# submitting the transaction
|
||||
# (default: "https://github.com/Shopify/active_merchant")
|
||||
#
|
||||
# Returns an EwayRapidResponse object, which conforms to the
|
||||
# ActiveMerchant::Billing::Response API, but also exposes #form_url.
|
||||
def setup_purchase(amount, options={})
|
||||
requires!(options, :redirect_url)
|
||||
request = build_xml_request("CreateAccessCodeRequest") do |doc|
|
||||
add_metadata(doc, options)
|
||||
add_invoice(doc, amount, options)
|
||||
add_customer_data(doc, options)
|
||||
end
|
||||
|
||||
commit(url_for("CreateAccessCode"), request)
|
||||
end
|
||||
|
||||
# Public: Retrieve the status of a transaction.
|
||||
#
|
||||
# identification - The Eway Rapid 3.0 access code for the transaction
|
||||
# (returned as the response.authorization by
|
||||
# #setup_purchase).
|
||||
#
|
||||
# Returns an EwayRapidResponse object.
|
||||
def status(identification)
|
||||
request = build_xml_request("GetAccessCodeResultRequest") do |doc|
|
||||
doc.AccessCode identification
|
||||
end
|
||||
commit(url_for("GetAccessCodeResult"), request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_purchase(identification, payment_method, endpoint)
|
||||
post = {
|
||||
"accesscode" => identification
|
||||
}
|
||||
add_credit_card(post, payment_method)
|
||||
|
||||
commit_form(endpoint, build_form_request(post))
|
||||
end
|
||||
|
||||
def add_metadata(doc, options)
|
||||
doc.RedirectUrl(options[:redirect_url])
|
||||
doc.CustomerIP options[:ip] if options[:ip]
|
||||
doc.Method "ProcessPayment"
|
||||
doc.DeviceID(options[:application_id] || application_id)
|
||||
end
|
||||
|
||||
def add_invoice(doc, money, options)
|
||||
doc.Payment do
|
||||
doc.TotalAmount amount(money)
|
||||
doc.InvoiceReference options[:order_id]
|
||||
doc.InvoiceDescription options[:description]
|
||||
currency_code = (options[:currency] || currency(money) || default_currency)
|
||||
doc.CurrencyCode currency_code
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_data(doc, options)
|
||||
doc.Customer do
|
||||
add_address(doc, (options[:billing_address] || options[:address]), {:email => options[:email]})
|
||||
end
|
||||
doc.ShippingAddress do
|
||||
add_address(doc, options[:shipping_address], {:skip_company => true})
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(doc, address, options={})
|
||||
return unless address
|
||||
if name = address[:name]
|
||||
parts = name.split(/\s+/)
|
||||
doc.FirstName parts.shift if parts.size > 1
|
||||
doc.LastName parts.join(" ")
|
||||
end
|
||||
doc.CompanyName address[:company] unless options[:skip_company]
|
||||
doc.Street1 address[:address1]
|
||||
doc.Street2 address[:address2]
|
||||
doc.City address[:city]
|
||||
doc.State address[:state]
|
||||
doc.PostalCode address[:zip]
|
||||
doc.Country address[:country]
|
||||
doc.Phone address[:phone]
|
||||
doc.Fax address[:fax]
|
||||
doc.Email options[:email]
|
||||
end
|
||||
|
||||
def add_credit_card(post, credit_card)
|
||||
post["cardname"] = credit_card.name
|
||||
post["cardnumber"] = credit_card.number
|
||||
post["cardexpirymonth"] = credit_card.month
|
||||
post["cardexpiryyear"] = credit_card.year
|
||||
post["cardcvn"] = credit_card.verification_value
|
||||
end
|
||||
|
||||
def build_xml_request(root)
|
||||
builder = Nokogiri::XML::Builder.new
|
||||
builder.__send__(root) do |doc|
|
||||
yield(doc)
|
||||
end
|
||||
builder.to_xml
|
||||
end
|
||||
|
||||
def build_form_request(post)
|
||||
request = []
|
||||
post.each do |key, value|
|
||||
request << "EWAY_#{key.upcase}=#{CGI.escape(value.to_s)}"
|
||||
end
|
||||
request.join("&")
|
||||
end
|
||||
|
||||
def url_for(action)
|
||||
(test? ? test_url : live_url) + action + ".xml"
|
||||
end
|
||||
|
||||
def commit(url, request, form_post=false)
|
||||
headers = {
|
||||
"Authorization" => ("Basic " + Base64.strict_encode64(@options[:login].to_s + ":" + @options[:password].to_s).chomp),
|
||||
"Content-Type" => "text/xml"
|
||||
}
|
||||
|
||||
raw = parse(ssl_post(url, request, headers))
|
||||
|
||||
succeeded = success?(raw)
|
||||
EwayRapidResponse.new(
|
||||
succeeded,
|
||||
message_from(succeeded, raw),
|
||||
raw,
|
||||
:authorization => authorization_from(raw),
|
||||
:test => test?,
|
||||
:avs_result => avs_result_from(raw),
|
||||
:cvv_result => cvv_result_from(raw)
|
||||
)
|
||||
rescue ActiveMerchant::ResponseError => e
|
||||
return EwayRapidResponse.new(false, e.response.message, {:status_code => e.response.code}, :test => test?)
|
||||
end
|
||||
|
||||
def commit_form(url, request)
|
||||
http_response = raw_ssl_request(:post, url, request)
|
||||
|
||||
success = (http_response.code.to_s == "302")
|
||||
message = (success ? "Succeeded" : http_response.body)
|
||||
if success
|
||||
authorization = CGI.unescape(http_response["Location"].split("=").last)
|
||||
end
|
||||
Response.new(success, message, {:location => http_response["Location"]}, :authorization => authorization, :test => test?)
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
|
||||
doc = Nokogiri::XML(xml)
|
||||
doc.root.xpath("*").each do |node|
|
||||
if (node.elements.size == 0)
|
||||
response[node.name.downcase.to_sym] = node.text
|
||||
else
|
||||
node.elements.each do |childnode|
|
||||
name = "#{node.name.downcase}_#{childnode.name.downcase}"
|
||||
response[name.to_sym] = childnode.text
|
||||
end
|
||||
end
|
||||
end unless doc.root.nil?
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
if response[:errors]
|
||||
false
|
||||
elsif response[:transactionstatus]
|
||||
(response[:transactionstatus] == "true")
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def message_from(succeeded, response)
|
||||
if response[:errors]
|
||||
response[:errors]
|
||||
elsif response[:responsecode]
|
||||
ActiveMerchant::Billing::EwayGateway::MESSAGES[response[:responsecode]]
|
||||
elsif response[:responsemessage]
|
||||
response[:responsemessage]
|
||||
elsif succeeded
|
||||
"Succeeded"
|
||||
else
|
||||
"Failed"
|
||||
end
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
response[:accesscode]
|
||||
end
|
||||
|
||||
def avs_result_from(response)
|
||||
code = case response[:verification_address]
|
||||
when "Valid"
|
||||
"M"
|
||||
when "Invalid"
|
||||
"N"
|
||||
else
|
||||
"I"
|
||||
end
|
||||
{:code => code}
|
||||
end
|
||||
|
||||
def cvv_result_from(response)
|
||||
case response[:verification_cvn]
|
||||
when "Valid"
|
||||
"M"
|
||||
when "Invalid"
|
||||
"N"
|
||||
else
|
||||
"P"
|
||||
end
|
||||
end
|
||||
|
||||
class EwayRapidResponse < ActiveMerchant::Billing::Response
|
||||
def form_url
|
||||
params["formactionurl"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
218
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/exact.rb
vendored
Normal file
218
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/exact.rb
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class ExactGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://secure2.e-xact.com/vplug-in/transaction/rpc-enc/service.asmx'
|
||||
|
||||
API_VERSION = "8.5"
|
||||
|
||||
TEST_LOGINS = [ {:login => "A00049-01", :password => "test1"},
|
||||
{:login => "A00427-01", :password => "testus"} ]
|
||||
|
||||
TRANSACTIONS = { :sale => "00",
|
||||
:authorization => "01",
|
||||
:capture => "32",
|
||||
:credit => "34" }
|
||||
|
||||
|
||||
ENVELOPE_NAMESPACES = { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
||||
'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
||||
}
|
||||
|
||||
SEND_AND_COMMIT_ATTRIBUTES = { 'xmlns:n1' => "http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/Request",
|
||||
'env:encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/'
|
||||
}
|
||||
|
||||
SEND_AND_COMMIT_SOURCE_ATTRIBUTES = { 'xmlns:n2' => 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes',
|
||||
'xsi:type' => 'n2:Transaction'
|
||||
}
|
||||
|
||||
POST_HEADERS = { 'soapAction' => "http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/SendAndCommit",
|
||||
'Content-Type' => 'text/xml'
|
||||
}
|
||||
|
||||
SUCCESS = "true"
|
||||
|
||||
SENSITIVE_FIELDS = [ :verification_str2, :expiry_date, :card_number ]
|
||||
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover]
|
||||
self.supported_countries = ['CA', 'US']
|
||||
self.homepage_url = 'http://www.e-xact.com'
|
||||
self.display_name = 'E-xact'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, credit_card, options = {})
|
||||
commit(:authorization, build_sale_or_authorization_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def purchase(money, credit_card, options = {})
|
||||
commit(:sale, build_sale_or_authorization_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
commit(:capture, build_capture_or_credit_request(money, authorization, options))
|
||||
end
|
||||
|
||||
def credit(money, authorization, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, authorization, options)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
commit(:credit, build_capture_or_credit_request(money, authorization, options))
|
||||
end
|
||||
|
||||
private
|
||||
def build_request(action, body)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
xml.instruct!
|
||||
xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
|
||||
xml.tag! 'env:Body' do
|
||||
xml.tag! 'n1:SendAndCommit', SEND_AND_COMMIT_ATTRIBUTES do
|
||||
xml.tag! 'SendAndCommitSource', SEND_AND_COMMIT_SOURCE_ATTRIBUTES do
|
||||
add_credentials(xml)
|
||||
add_transaction_type(xml, action)
|
||||
xml << body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_sale_or_authorization_request(money, credit_card, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
add_amount(xml, money)
|
||||
add_credit_card(xml, credit_card)
|
||||
add_customer_data(xml, options)
|
||||
add_invoice(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_capture_or_credit_request(money, identification, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
add_identification(xml, identification)
|
||||
add_amount(xml, money)
|
||||
add_customer_data(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def add_credentials(xml)
|
||||
xml.tag! 'ExactID', @options[:login]
|
||||
xml.tag! 'Password', @options[:password]
|
||||
end
|
||||
|
||||
def add_transaction_type(xml, action)
|
||||
xml.tag! 'Transaction_Type', TRANSACTIONS[action]
|
||||
end
|
||||
|
||||
def add_identification(xml, identification)
|
||||
authorization_num, transaction_tag = identification.split(';')
|
||||
|
||||
xml.tag! 'Authorization_Num', authorization_num
|
||||
xml.tag! 'Transaction_Tag', transaction_tag
|
||||
end
|
||||
|
||||
def add_amount(xml, money)
|
||||
xml.tag! 'DollarAmount', amount(money)
|
||||
end
|
||||
|
||||
def add_credit_card(xml, credit_card)
|
||||
xml.tag! 'Card_Number', credit_card.number
|
||||
xml.tag! 'Expiry_Date', expdate(credit_card)
|
||||
xml.tag! 'CardHoldersName', credit_card.name
|
||||
|
||||
if credit_card.verification_value?
|
||||
xml.tag! 'CVD_Presence_Ind', '1'
|
||||
xml.tag! 'VerificationStr2', credit_card.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_data(xml, options)
|
||||
xml.tag! 'Customer_Ref', options[:customer]
|
||||
xml.tag! 'Client_IP', options[:ip]
|
||||
xml.tag! 'Client_Email', options[:email]
|
||||
end
|
||||
|
||||
def add_address(xml, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
xml.tag! 'ZipCode', address[:zip]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(xml, options)
|
||||
xml.tag! 'Reference_No', options[:order_id]
|
||||
xml.tag! 'Reference_3', options[:description]
|
||||
end
|
||||
|
||||
def expdate(credit_card)
|
||||
"#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
|
||||
end
|
||||
|
||||
def commit(action, request)
|
||||
response = parse(ssl_post(self.live_url, build_request(action, request), POST_HEADERS))
|
||||
|
||||
Response.new(successful?(response), message_from(response), response,
|
||||
:test => test?,
|
||||
:authorization => authorization_from(response),
|
||||
:avs_result => { :code => response[:avs] },
|
||||
:cvv_result => response[:cvv2]
|
||||
)
|
||||
end
|
||||
|
||||
def successful?(response)
|
||||
response[:transaction_approved] == SUCCESS
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
if response[:authorization_num] && response[:transaction_tag]
|
||||
"#{response[:authorization_num]};#{response[:transaction_tag]}"
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
if response[:faultcode] && response[:faultstring]
|
||||
response[:faultstring]
|
||||
elsif response[:error_number] != '0'
|
||||
response[:error_description]
|
||||
else
|
||||
result = response[:exact_message] || ''
|
||||
result << " - #{response[:bank_message]}" unless response[:bank_message].blank?
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
|
||||
if root = REXML::XPath.first(xml, "//types:TransactionResult")
|
||||
parse_elements(response, root)
|
||||
elsif root = REXML::XPath.first(xml, "//soap:Fault")
|
||||
parse_elements(response, root)
|
||||
end
|
||||
|
||||
response.delete_if{ |k,v| SENSITIVE_FIELDS.include?(k) }
|
||||
end
|
||||
|
||||
def parse_elements(response, root)
|
||||
root.elements.to_a.each do |node|
|
||||
response[node.name.gsub(/EXact/, 'Exact').underscore.to_sym] = (node.text || '').strip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
152
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/fat_zebra.rb
vendored
Normal file
152
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/fat_zebra.rb
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
require 'json'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class FatZebraGateway < Gateway
|
||||
self.live_url = "https://gateway.fatzebra.com.au/v1.0"
|
||||
self.test_url = "https://gateway.sandbox.fatzebra.com.au/v1.0"
|
||||
|
||||
self.supported_countries = ['AU']
|
||||
self.default_currency = 'AUD'
|
||||
self.money_format = :cents
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :jcb]
|
||||
|
||||
self.homepage_url = 'https://www.fatzebra.com.au/'
|
||||
self.display_name = 'Fat Zebra'
|
||||
|
||||
# Setup a new instance of the gateway.
|
||||
#
|
||||
# The options hash should include :username and :token
|
||||
# You can find your username and token at https://dashboard.fatzebra.com.au
|
||||
# Under the Your Account section
|
||||
def initialize(options = {})
|
||||
requires!(options, :username)
|
||||
requires!(options, :token)
|
||||
@username = options[:username]
|
||||
@token = options[:token]
|
||||
super
|
||||
end
|
||||
|
||||
# To create a purchase on a credit card use:
|
||||
#
|
||||
# purchase(money, creditcard , { ... })
|
||||
#
|
||||
# To charge a tokenized card
|
||||
#
|
||||
# purchase(money, {:token => "abzy87u", :cvv => "123"}, { ... }})
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = {}
|
||||
|
||||
add_amount(post, money, options)
|
||||
add_creditcard(post, creditcard, options)
|
||||
post[:reference] = options[:order_id]
|
||||
post[:customer_ip] = options[:ip]
|
||||
|
||||
commit(:post, 'purchases', post)
|
||||
end
|
||||
|
||||
# Refund a transaction
|
||||
#
|
||||
# amount - Integer - the amount to refund
|
||||
# txn_id - String - the original transaction to be refunded
|
||||
# reference - String - your transaction reference
|
||||
def refund(money, txn_id, reference)
|
||||
post = {}
|
||||
|
||||
post[:amount] = money
|
||||
post[:transaction_id] = txn_id
|
||||
post[:reference] = reference
|
||||
|
||||
commit(:post, "refunds", post)
|
||||
end
|
||||
|
||||
# Tokenize a credit card
|
||||
def store(creditcard)
|
||||
post = {}
|
||||
add_creditcard(post, creditcard)
|
||||
|
||||
commit(:post, "credit_cards", post)
|
||||
end
|
||||
|
||||
private
|
||||
# Add the money details to the request
|
||||
def add_amount(post, money, options)
|
||||
post[:amount] = money
|
||||
end
|
||||
|
||||
# Add the credit card details to the request
|
||||
def add_creditcard(post, creditcard, options = {})
|
||||
if creditcard.respond_to?(:number)
|
||||
post[:card_number] = creditcard.number
|
||||
post[:card_expiry] = "#{creditcard.month}/#{creditcard.year}"
|
||||
post[:cvv] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:card_holder] = creditcard.name if creditcard.name
|
||||
else
|
||||
post[:card_token] = creditcard[:token]
|
||||
post[:cvv] = creditcard[:cvv]
|
||||
end
|
||||
end
|
||||
|
||||
# Post the data to the gateway
|
||||
def commit(method, uri, parameters=nil)
|
||||
raw_response = response = nil
|
||||
success = false
|
||||
begin
|
||||
raw_response = ssl_request(method, get_url(uri), parameters.to_json, headers)
|
||||
response = parse(raw_response)
|
||||
success = response["successful"] && (response["response"]["successful"] || response["response"]["token"])
|
||||
rescue ResponseError => e
|
||||
if e.response.code == "401"
|
||||
return Response.new(false, "Invalid Login")
|
||||
end
|
||||
|
||||
raw_response = e.response.body
|
||||
response = parse(raw_response)
|
||||
rescue JSON::ParserError
|
||||
response = json_error(raw_response)
|
||||
end
|
||||
|
||||
message = response["response"]["message"]
|
||||
unless response["successful"]
|
||||
# There is an error, so we will show that instead
|
||||
message = response["errors"].empty? ? "Unknown Error" : response["errors"].join(", ")
|
||||
end
|
||||
|
||||
Response.new(success,
|
||||
message,
|
||||
response,
|
||||
:test => response.has_key?("test") ? response["test"] : false,
|
||||
:authorization => response["response"]["id"] || response["response"]["token"])
|
||||
end
|
||||
|
||||
# Parse the returned JSON, if parse errors are raised then return a detailed error.
|
||||
def parse(response)
|
||||
begin
|
||||
JSON.parse(response)
|
||||
rescue JSON::ParserError
|
||||
msg = 'Invalid JSON response received from Fat Zebra. Please contact support@fatzebra.com.au if you continue to receive this message.'
|
||||
msg += " (The raw response returned by the API was #{response.inspect})"
|
||||
{
|
||||
"successful" => false,
|
||||
"response" => {},
|
||||
"errors" => [msg]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Build the URL based on the AM mode and the URI
|
||||
def get_url(uri)
|
||||
base = test? ? self.test_url : self.live_url
|
||||
base + "/" + uri
|
||||
end
|
||||
|
||||
# Builds the auth and U-A headers for the request
|
||||
def headers
|
||||
{
|
||||
"Authorization" => "Basic " + Base64.strict_encode64(@username.to_s + ":" + @token.to_s).strip,
|
||||
"User-Agent" => "Fat Zebra v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
167
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/federated_canada.rb
vendored
Normal file
167
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/federated_canada.rb
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class FederatedCanadaGateway < Gateway
|
||||
# Same URL for both test and live, testing is done by using the test username (demo) and password (password).
|
||||
self.live_url = self.test_url = 'https://secure.federatedgateway.com/api/transact.php'
|
||||
|
||||
APPROVED, DECLINED, ERROR = 1, 2, 3
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['CA']
|
||||
|
||||
self.default_currency = 'CAD'
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.federatedcanada.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Federated Canada'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('sale', money, post)
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
commit('auth', money, post)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
options[:transactionid] = authorization
|
||||
commit('capture', money, options)
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
options[:transactionid] = authorization
|
||||
commit('void', nil, options)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
commit('refund', money, options.merge(:transactionid => authorization))
|
||||
end
|
||||
|
||||
def credit(money, authorization, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, authorization, options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:firstname] = options[:first_name]
|
||||
post[:lastname] = options[:last_name]
|
||||
|
||||
post[:email] = options[:email]
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = (options[:billing_address] || options[:address])
|
||||
post[:company] = address[:company]
|
||||
post[:address1] = address[:address1]
|
||||
post[:address2] = address[:address2]
|
||||
post[:city] = address[:city]
|
||||
post[:state] = address[:state]
|
||||
post[:zip] = address[:zip]
|
||||
post[:country] = address[:country]
|
||||
post[:phone] = address[:phone]
|
||||
end
|
||||
if address = options[:shipping_address]
|
||||
post[:shipping_firstname] = address[:first_name]
|
||||
post[:shipping_lastname] = address[:last_name]
|
||||
post[:shipping_company] = address[:company]
|
||||
post[:shipping_address1] = address[:address1]
|
||||
post[:shipping_address2] = address[:address2]
|
||||
post[:shipping_city] = address[:city]
|
||||
post[:shipping_state] = address[:state]
|
||||
post[:shipping_zip] = address[:zip]
|
||||
post[:shipping_country] = address[:country]
|
||||
post[:shipping_email] = address[:email]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:orderid] = options[:order_id]
|
||||
post[:orderdescription] = options[:description]
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:ccnumber] = creditcard.number
|
||||
post[:ccexp] = expdate(creditcard)
|
||||
post[:cvv] = creditcard.verification_value
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = sprintf("%.4i", creditcard.year)
|
||||
month = sprintf("%.2i", creditcard.month)
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
body.split('&').inject({}) do |memo, x|
|
||||
k, v = x.split('=')
|
||||
memo[k] = v
|
||||
memo
|
||||
end
|
||||
end
|
||||
|
||||
def commit(action, money, parameters)
|
||||
parameters[:amount] = amount(money)
|
||||
data = ssl_post(self.live_url, post_data(action, parameters))
|
||||
response = parse(data)
|
||||
message = message_from(response)
|
||||
test_mode = test?
|
||||
|
||||
Response.new(success?(response), message, response,
|
||||
:test => test?,
|
||||
:authorization => response['transactionid'],
|
||||
:avs_result => {:code => response['avsresponse']},
|
||||
:cvv_result => response['cvvresponse']
|
||||
)
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response['response'] == '1'
|
||||
end
|
||||
|
||||
def test?
|
||||
(@options[:login].eql?('demo')) && (@options[:password].eql?('password'))
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
case response['response'].to_i
|
||||
when APPROVED
|
||||
"Transaction Approved"
|
||||
when DECLINED
|
||||
"Transaction Declined"
|
||||
else
|
||||
"Error in transaction data or system error"
|
||||
end
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
parameters[:type] = action
|
||||
parameters[:username] = @options[:login]
|
||||
parameters[:password] = @options[:password]
|
||||
parameters.map{|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/finansbank.rb
vendored
Normal file
22
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/finansbank.rb
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
require File.dirname(__FILE__) + '/cc5'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class FinansbankGateway < CC5Gateway
|
||||
self.live_url = self.test_url = 'https://www.fbwebpos.com/servlet/cc5ApiServer'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US', 'TR']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'https://www.fbwebpos.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Finansbank WebPOS'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
176
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/first_pay.rb
vendored
Normal file
176
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/first_pay.rb
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class FirstPayGateway < Gateway
|
||||
class FirstPayPostData < PostData
|
||||
# Fields that will be sent even if they are blank
|
||||
self.required_fields = [ :action, :amount, :trackid ]
|
||||
end
|
||||
|
||||
# both URLs are IP restricted
|
||||
self.test_url = 'https://apgcert.first-pay.com/AcqENGIN/SecureCapture'
|
||||
self.live_url = 'https://acqengin.first-pay.com/AcqENGIN/SecureCapture'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.first-pay.com'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'First Pay'
|
||||
|
||||
# all transactions are in cents
|
||||
self.money_format = :cents
|
||||
|
||||
ACTIONS = {
|
||||
'sale' => 1,
|
||||
'credit' => 2,
|
||||
'void' => 3
|
||||
}
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = FirstPayPostData.new
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('sale', money, post)
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
requires!(options, :credit_card)
|
||||
|
||||
post = FirstPayPostData.new
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, options[:credit_card])
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
add_credit_data(post, reference)
|
||||
|
||||
commit('credit', money, post)
|
||||
end
|
||||
|
||||
def credit(money, reference, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, reference, options)
|
||||
end
|
||||
|
||||
def void(money, creditcard, options = {})
|
||||
post = FirstPayPostData.new
|
||||
add_creditcard(post, creditcard)
|
||||
add_void_data(post, options)
|
||||
add_invoice(post, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('void', money, post)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:cardip] = options[:ip]
|
||||
post[:email] = options[:email]
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
post[:addr] = billing_address[:address1].to_s + ' ' + billing_address[:address2].to_s
|
||||
post[:city] = billing_address[:city]
|
||||
post[:state] = billing_address[:state]
|
||||
post[:zip] = billing_address[:zip]
|
||||
post[:country] = billing_address[:country]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:trackid] = rand(Time.now.to_i)
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:member] = creditcard.first_name.to_s + " " + creditcard.last_name.to_s
|
||||
post[:card] = creditcard.number
|
||||
post[:exp] = expdate(creditcard)
|
||||
end
|
||||
|
||||
def expdate(credit_card)
|
||||
year = sprintf("%.4i", credit_card.year)
|
||||
month = sprintf("%.2i", credit_card.month)
|
||||
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def add_credit_data(post, transaction_id)
|
||||
post[:transid] = transaction_id
|
||||
end
|
||||
|
||||
def add_void_data(post, options)
|
||||
post[:transid] = options[:transactionid]
|
||||
end
|
||||
|
||||
def commit(action, money, post)
|
||||
response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(action, post, money)) )
|
||||
|
||||
Response.new(response[:response] == 'CAPTURED', response[:message], response,
|
||||
:test => test?,
|
||||
:authorization => response[:authorization],
|
||||
:avs_result => { :code => response[:avsresponse] },
|
||||
:cvv_result => response[:cvvresponse])
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
response = {}
|
||||
|
||||
# check for an error first
|
||||
if body.include?('!ERROR!')
|
||||
response[:response] = 'ERROR'
|
||||
response[:message] = error_message_from(body)
|
||||
else
|
||||
# a capture / not captured response will be : delimited
|
||||
split = body.split(':')
|
||||
response[:response] = split[0]
|
||||
|
||||
# FirstPay docs are worthless. turns out the transactionid is required for credits
|
||||
# so we need to store that in authorization, not the actual auth.
|
||||
if response[:response] == 'CAPTURED'
|
||||
response[:message] = 'CAPTURED'
|
||||
response[:authorization] = split[9] # actually the transactionid
|
||||
response[:auth] = split[1]
|
||||
response[:avsresponse] = split[3]
|
||||
response[:cvvresponse] = split[17]
|
||||
else
|
||||
# NOT CAPTURED response
|
||||
response[:message] = split[1]
|
||||
response[:transactionid] = split[9]
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
def error_message_from(response)
|
||||
# error messages use this format - '!ERROR! 704-MISSING BASIC DATA TYPE:card, exp, zip, addr, member, amount\n'
|
||||
response.split("! ")[1].chomp
|
||||
end
|
||||
|
||||
def post_data(action, post, money)
|
||||
post[:vid] = @options[:login]
|
||||
post[:password] = @options[:password]
|
||||
post[:action] = ACTIONS[action]
|
||||
post[:amount] = amount(money)
|
||||
|
||||
return post.to_post_data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
314
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/firstdata_e4.rb
vendored
Normal file
314
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/firstdata_e4.rb
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class FirstdataE4Gateway < Gateway
|
||||
# TransArmor support requires v11 or lower
|
||||
self.test_url = "https://api.demo.globalgatewaye4.firstdata.com/transaction/v11"
|
||||
self.live_url = "https://api.globalgatewaye4.firstdata.com/transaction/v11"
|
||||
|
||||
TRANSACTIONS = {
|
||||
:sale => "00",
|
||||
:authorization => "01",
|
||||
:capture => "32",
|
||||
:void => "33",
|
||||
:credit => "34",
|
||||
:store => "05"
|
||||
}
|
||||
|
||||
POST_HEADERS = {
|
||||
"Accepts" => "application/xml",
|
||||
"Content-Type" => "application/xml"
|
||||
}
|
||||
|
||||
SUCCESS = "true"
|
||||
|
||||
SENSITIVE_FIELDS = [:verification_str2, :expiry_date, :card_number]
|
||||
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover]
|
||||
self.supported_countries = ["CA", "US"]
|
||||
self.default_currency = "USD"
|
||||
self.homepage_url = "http://www.firstdata.com"
|
||||
self.display_name = "FirstData Global Gateway e4"
|
||||
|
||||
# Create a new FirstdataE4Gateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The EXACT ID. Also known as the Gateway ID.
|
||||
# (Found in your administration terminal settings)
|
||||
# * <tt>:password</tt> -- The terminal password (not your account password)
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
@options = options
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, credit_card_or_store_authorization, options = {})
|
||||
commit(:authorization, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
|
||||
end
|
||||
|
||||
def purchase(money, credit_card_or_store_authorization, options = {})
|
||||
commit(:sale, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
commit(:capture, build_capture_or_credit_request(money, authorization, options))
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
commit(:void, build_capture_or_credit_request(money_from_authorization(authorization), authorization, options))
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
commit(:credit, build_capture_or_credit_request(money, authorization, options))
|
||||
end
|
||||
|
||||
# Tokenize a credit card with TransArmor
|
||||
#
|
||||
# The TransArmor token and other card data necessary for subsequent
|
||||
# transactions is stored in the response's +authorization+ attribute.
|
||||
# The authorization string may be passed to +authorize+ and +purchase+
|
||||
# instead of a +ActiveMerchant::Billing::CreditCard+ instance.
|
||||
#
|
||||
# TransArmor support must be explicitly activated on your gateway
|
||||
# account by FirstData. If your authorization string is empty, contact
|
||||
# FirstData support for account setup assistance.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# # Generate token
|
||||
# result = gateway.store(credit_card)
|
||||
# if result.success?
|
||||
# my_record.update_attributes(:authorization => result.authorization)
|
||||
# end
|
||||
#
|
||||
# # Use token
|
||||
# result = gateway.purchase(1000, my_record.authorization)
|
||||
#
|
||||
# https://firstdata.zendesk.com/entries/21303361-transarmor-tokenization
|
||||
def store(credit_card, options = {})
|
||||
commit(:store, build_store_request(credit_card, options), credit_card)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_request(action, body)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
xml.instruct!
|
||||
xml.tag! "Transaction" do
|
||||
add_credentials(xml)
|
||||
add_transaction_type(xml, action)
|
||||
xml << body
|
||||
end
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_sale_or_authorization_request(money, credit_card_or_store_authorization, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
add_amount(xml, money)
|
||||
|
||||
if credit_card_or_store_authorization.is_a? String
|
||||
add_credit_card_token(xml, credit_card_or_store_authorization)
|
||||
else
|
||||
add_credit_card(xml, credit_card_or_store_authorization)
|
||||
end
|
||||
|
||||
add_customer_data(xml, options)
|
||||
add_invoice(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_capture_or_credit_request(money, identification, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
add_identification(xml, identification)
|
||||
add_amount(xml, money)
|
||||
add_customer_data(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_store_request(credit_card, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
add_credit_card(xml, credit_card)
|
||||
add_customer_data(xml, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def add_credentials(xml)
|
||||
xml.tag! "ExactID", @options[:login]
|
||||
xml.tag! "Password", @options[:password]
|
||||
end
|
||||
|
||||
def add_transaction_type(xml, action)
|
||||
xml.tag! "Transaction_Type", TRANSACTIONS[action]
|
||||
end
|
||||
|
||||
def add_identification(xml, identification)
|
||||
authorization_num, transaction_tag, _ = identification.split(";")
|
||||
|
||||
xml.tag! "Authorization_Num", authorization_num
|
||||
xml.tag! "Transaction_Tag", transaction_tag
|
||||
end
|
||||
|
||||
def add_amount(xml, money)
|
||||
xml.tag! "DollarAmount", amount(money)
|
||||
end
|
||||
|
||||
def add_credit_card(xml, credit_card)
|
||||
xml.tag! "Card_Number", credit_card.number
|
||||
xml.tag! "Expiry_Date", expdate(credit_card)
|
||||
xml.tag! "CardHoldersName", credit_card.name
|
||||
xml.tag! "CardType", credit_card.brand
|
||||
|
||||
if credit_card.verification_value?
|
||||
xml.tag! "CVD_Presence_Ind", "1"
|
||||
xml.tag! "VerificationStr2", credit_card.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_card_token(xml, store_authorization)
|
||||
params = store_authorization.split(";")
|
||||
credit_card = CreditCard.new(
|
||||
:brand => params[1],
|
||||
:first_name => params[2],
|
||||
:last_name => params[3],
|
||||
:month => params[4],
|
||||
:year => params[5])
|
||||
|
||||
xml.tag! "TransarmorToken", params[0]
|
||||
xml.tag! "Expiry_Date", expdate(credit_card)
|
||||
xml.tag! "CardHoldersName", credit_card.name
|
||||
xml.tag! "CardType", credit_card.brand
|
||||
end
|
||||
|
||||
def add_customer_data(xml, options)
|
||||
xml.tag! "Customer_Ref", options[:customer] if options[:customer]
|
||||
xml.tag! "Client_IP", options[:ip] if options[:ip]
|
||||
xml.tag! "Client_Email", options[:email] if options[:email]
|
||||
end
|
||||
|
||||
def add_address(xml, options)
|
||||
if address = (options[:billing_address] || options[:address])
|
||||
xml.tag! "ZipCode", address[:zip]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(xml, options)
|
||||
xml.tag! "Reference_No", options[:order_id]
|
||||
xml.tag! "Reference_3", options[:description] if options[:description]
|
||||
end
|
||||
|
||||
def expdate(credit_card)
|
||||
"#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
|
||||
end
|
||||
|
||||
def commit(action, request, credit_card = nil)
|
||||
url = (test? ? self.test_url : self.live_url)
|
||||
begin
|
||||
response = parse(ssl_post(url, build_request(action, request), POST_HEADERS))
|
||||
rescue ResponseError => e
|
||||
response = parse_error(e.response)
|
||||
end
|
||||
|
||||
Response.new(successful?(response), message_from(response), response,
|
||||
:test => test?,
|
||||
:authorization => response_authorization(action, response, credit_card),
|
||||
:avs_result => {:code => response[:avs]},
|
||||
:cvv_result => response[:cvv2]
|
||||
)
|
||||
end
|
||||
|
||||
def successful?(response)
|
||||
response[:transaction_approved] == SUCCESS
|
||||
end
|
||||
|
||||
def response_authorization(action, response, credit_card)
|
||||
if action == :store
|
||||
store_authorization_from(response, credit_card)
|
||||
else
|
||||
authorization_from(response)
|
||||
end
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
if response[:authorization_num] && response[:transaction_tag]
|
||||
[
|
||||
response[:authorization_num],
|
||||
response[:transaction_tag],
|
||||
(response[:dollar_amount].to_f * 100).to_i
|
||||
].join(";")
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def store_authorization_from(response, credit_card)
|
||||
if response[:transarmor_token].present?
|
||||
[
|
||||
response[:transarmor_token],
|
||||
credit_card.brand,
|
||||
credit_card.first_name,
|
||||
credit_card.last_name,
|
||||
credit_card.month,
|
||||
credit_card.year
|
||||
].map { |value| value.to_s.gsub(/;/, "") }.join(";")
|
||||
else
|
||||
raise StandardError, "TransArmor support is not enabled on your #{display_name} account"
|
||||
end
|
||||
end
|
||||
|
||||
def money_from_authorization(auth)
|
||||
_, _, amount = auth.split(/;/, 3)
|
||||
amount.to_i # return the # of cents, no need to divide
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
if(response[:faultcode] && response[:faultstring])
|
||||
response[:faultstring]
|
||||
elsif(response[:error_number] && response[:error_number] != "0")
|
||||
response[:error_description]
|
||||
else
|
||||
result = (response[:exact_message] || "")
|
||||
result << " - #{response[:bank_message]}" if response[:bank_message].present?
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
def parse_error(error)
|
||||
{
|
||||
:transaction_approved => "false",
|
||||
:error_number => error.code,
|
||||
:error_description => error.body
|
||||
}
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
|
||||
if root = REXML::XPath.first(xml, "//TransactionResult")
|
||||
parse_elements(response, root)
|
||||
end
|
||||
|
||||
response.delete_if{ |k,v| SENSITIVE_FIELDS.include?(k) }
|
||||
end
|
||||
|
||||
def parse_elements(response, root)
|
||||
root.elements.to_a.each do |node|
|
||||
response[node.name.gsub(/EXact/, "Exact").underscore.to_sym] = (node.text || "").strip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
257
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/garanti.rb
vendored
Normal file
257
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/garanti.rb
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class GarantiGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://sanalposprov.garanti.com.tr/VPServlet'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US','TR']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'https://sanalposweb.garanti.com.tr'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Garanti Sanal POS'
|
||||
|
||||
self.default_currency = 'TRL'
|
||||
|
||||
self.money_format = :cents
|
||||
|
||||
CURRENCY_CODES = {
|
||||
'YTL' => 949,
|
||||
'TRL' => 949,
|
||||
'TL' => 949,
|
||||
'USD' => 840,
|
||||
'EUR' => 978,
|
||||
'GBP' => 826,
|
||||
'JPY' => 392
|
||||
}
|
||||
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :terminal_id, :merchant_id)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, credit_card, options = {})
|
||||
options = options.merge(:gvp_order_type => "sales")
|
||||
commit(money, build_sale_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def authorize(money, credit_card, options = {})
|
||||
options = options.merge(:gvp_order_type => "preauth")
|
||||
commit(money, build_authorize_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def capture(money, ref_id, options = {})
|
||||
options = options.merge(:gvp_order_type => "postauth")
|
||||
commit(money, build_capture_request(money, ref_id, options))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def security_data
|
||||
rjusted_terminal_id = @options[:terminal_id].to_s.rjust(9, "0")
|
||||
Digest::SHA1.hexdigest(@options[:password].to_s + rjusted_terminal_id).upcase
|
||||
end
|
||||
|
||||
def generate_hash_data(order_id, terminal_id, credit_card_number, amount, security_data)
|
||||
data = [order_id, terminal_id, credit_card_number, amount, security_data].join
|
||||
Digest::SHA1.hexdigest(data).upcase
|
||||
end
|
||||
|
||||
def build_xml_request(money, credit_card, options, &block)
|
||||
card_number = credit_card.respond_to?(:number) ? credit_card.number : ''
|
||||
hash_data = generate_hash_data(format_order_id(options[:order_id]), @options[:terminal_id], card_number, amount(money), security_data)
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
|
||||
|
||||
xml.tag! 'GVPSRequest' do
|
||||
xml.tag! 'Mode', test? ? 'TEST' : 'PROD'
|
||||
xml.tag! 'Version', 'V0.01'
|
||||
xml.tag! 'Terminal' do
|
||||
xml.tag! 'ProvUserID', 'PROVAUT'
|
||||
xml.tag! 'HashData', hash_data
|
||||
xml.tag! 'UserID', @options[:login]
|
||||
xml.tag! 'ID', @options[:terminal_id]
|
||||
xml.tag! 'MerchantID', @options[:merchant_id]
|
||||
end
|
||||
|
||||
if block_given?
|
||||
yield xml
|
||||
else
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_sale_request(money, credit_card, options)
|
||||
build_xml_request(money, credit_card, options) do |xml|
|
||||
add_customer_data(xml, options)
|
||||
add_order_data(xml, options) do |xml|
|
||||
add_addresses(xml, options)
|
||||
end
|
||||
add_credit_card(xml, credit_card)
|
||||
add_transaction_data(xml, money, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def build_authorize_request(money, credit_card, options)
|
||||
build_xml_request(money, credit_card, options) do |xml|
|
||||
add_customer_data(xml, options)
|
||||
add_order_data(xml, options) do |xml|
|
||||
add_addresses(xml, options)
|
||||
end
|
||||
add_credit_card(xml, credit_card)
|
||||
add_transaction_data(xml, money, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def build_capture_request(money, ref_id, options)
|
||||
options = options.merge(:order_id => ref_id)
|
||||
build_xml_request(money, ref_id, options) do |xml|
|
||||
add_customer_data(xml, options)
|
||||
add_order_data(xml, options)
|
||||
add_transaction_data(xml, money, options)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_data(xml, options)
|
||||
xml.tag! 'Customer' do
|
||||
xml.tag! 'IPAddress', options[:ip] || '1.1.1.1'
|
||||
xml.tag! 'EmailAddress', options[:email]
|
||||
end
|
||||
end
|
||||
|
||||
def add_order_data(xml, options, &block)
|
||||
xml.tag! 'Order' do
|
||||
xml.tag! 'OrderID', format_order_id(options[:order_id])
|
||||
xml.tag! 'GroupID'
|
||||
|
||||
if block_given?
|
||||
yield xml
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_card(xml, credit_card)
|
||||
xml.tag! 'Card' do
|
||||
xml.tag! 'Number', credit_card.number
|
||||
xml.tag! 'ExpireDate', [format_exp(credit_card.month), format_exp(credit_card.year)].join
|
||||
xml.tag! 'CVV2', credit_card.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def format_exp(value)
|
||||
format(value, :two_digits)
|
||||
end
|
||||
|
||||
# OrderId field must be A-Za-z0-9_ format and max 36 char
|
||||
def format_order_id(order_id)
|
||||
order_id.to_s.gsub(/[^A-Za-z0-9_]/, '')[0...36]
|
||||
end
|
||||
|
||||
def add_addresses(xml, options)
|
||||
xml.tag! 'AddressList' do
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
xml.tag! 'Address' do
|
||||
xml.tag! 'Type', 'B'
|
||||
add_address(xml, billing_address)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:shipping_address]
|
||||
xml.tag! 'Address' do
|
||||
xml.tag! 'Type', 'S'
|
||||
add_address(xml, options[:shipping_address])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(xml, address)
|
||||
xml.tag! 'Name', normalize(address[:name])
|
||||
address_text = address[:address1]
|
||||
address_text << " #{ address[:address2]}" if address[:address2]
|
||||
xml.tag! 'Text', normalize(address_text)
|
||||
xml.tag! 'City', normalize(address[:city])
|
||||
xml.tag! 'District', normalize(address[:state])
|
||||
xml.tag! 'PostalCode', address[:zip]
|
||||
xml.tag! 'Country', normalize(address[:country])
|
||||
xml.tag! 'Company', normalize(address[:company])
|
||||
xml.tag! 'PhoneNumber', address[:phone].to_s.gsub(/[^0-9]/, '') if address[:phone]
|
||||
end
|
||||
|
||||
def normalize(text)
|
||||
return unless text
|
||||
|
||||
if ActiveSupport::Inflector.method(:transliterate).arity == -2
|
||||
ActiveSupport::Inflector.transliterate(text,'')
|
||||
elsif RUBY_VERSION >= '1.9'
|
||||
text.gsub(/[^\x00-\x7F]+/, '')
|
||||
else
|
||||
ActiveSupport::Inflector.transliterate(text).to_s
|
||||
end
|
||||
end
|
||||
|
||||
def add_transaction_data(xml, money, options)
|
||||
xml.tag! 'Transaction' do
|
||||
xml.tag! 'Type', options[:gvp_order_type]
|
||||
xml.tag! 'Amount', amount(money)
|
||||
xml.tag! 'CurrencyCode', currency_code(options[:currency] || currency(money))
|
||||
xml.tag! 'CardholderPresentCode', 0
|
||||
end
|
||||
end
|
||||
|
||||
def currency_code(currency)
|
||||
CURRENCY_CODES[currency] || CURRENCY_CODES[default_currency]
|
||||
end
|
||||
|
||||
def commit(money,request)
|
||||
raw_response = ssl_post(self.live_url, "data=" + request)
|
||||
response = parse(raw_response)
|
||||
|
||||
success = success?(response)
|
||||
|
||||
Response.new(success,
|
||||
success ? 'Approved' : "Declined (Reason: #{response[:reason_code]} - #{response[:error_msg]} - #{response[:sys_err_msg]})",
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => response[:order_id])
|
||||
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
|
||||
|
||||
def success?(response)
|
||||
response[:message] == "Approved"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
207
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/hdfc.rb
vendored
Normal file
207
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/hdfc.rb
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
require "nokogiri"
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class HdfcGateway < Gateway
|
||||
self.display_name = "HDFC"
|
||||
self.homepage_url = "http://www.hdfcbank.com/sme/sme-details/merchant-services/guzh6m0i"
|
||||
|
||||
self.test_url = "https://securepgtest.fssnet.co.in/pgway/servlet/"
|
||||
self.live_url = "https://securepg.fssnet.co.in/pgway/servlet/"
|
||||
|
||||
self.supported_countries = ["IN"]
|
||||
self.default_currency = "INR"
|
||||
self.money_format = :dollars
|
||||
self.supported_cardtypes = [:visa, :master, :discover, :diners_club]
|
||||
|
||||
def initialize(options={})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(amount, payment_method, options={})
|
||||
post = {}
|
||||
add_invoice(post, amount, options)
|
||||
add_payment_method(post, payment_method)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit("purchase", post)
|
||||
end
|
||||
|
||||
def authorize(amount, payment_method, options={})
|
||||
post = {}
|
||||
add_invoice(post, amount, options)
|
||||
add_payment_method(post, payment_method)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit("authorize", post)
|
||||
end
|
||||
|
||||
def capture(amount, authorization, options={})
|
||||
post = {}
|
||||
add_invoice(post, amount, options)
|
||||
add_reference(post, authorization)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit("capture", post)
|
||||
end
|
||||
|
||||
def refund(amount, authorization, options={})
|
||||
post = {}
|
||||
add_invoice(post, amount, options)
|
||||
add_reference(post, authorization)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit("refund", post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency for HDFC: #{k}")}
|
||||
CURRENCY_CODES["AED"] = "784"
|
||||
CURRENCY_CODES["AUD"] = "036"
|
||||
CURRENCY_CODES["CAD"] = "124"
|
||||
CURRENCY_CODES["EUR"] = "978"
|
||||
CURRENCY_CODES["GBP"] = "826"
|
||||
CURRENCY_CODES["INR"] = "356"
|
||||
CURRENCY_CODES["OMR"] = "512"
|
||||
CURRENCY_CODES["QAR"] = "634"
|
||||
CURRENCY_CODES["SGD"] = "702"
|
||||
CURRENCY_CODES["USD"] = "840"
|
||||
|
||||
def add_invoice(post, amount, options)
|
||||
post[:amt] = amount(amount)
|
||||
post[:currencycode] = CURRENCY_CODES[options[:currency] || currency(amount)]
|
||||
post[:trackid] = escape(options[:order_id], 40) if options[:order_id]
|
||||
post[:udf1] = escape(options[:description]) if options[:description]
|
||||
post[:eci] = options[:eci] if options[:eci]
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:udf2] = escape(options[:email]) if options[:email]
|
||||
if address = (options[:billing_address] || options[:address])
|
||||
post[:udf3] = escape(address[:phone]) if address[:phone]
|
||||
post[:udf4] = escape(<<EOA)
|
||||
#{address[:name]}
|
||||
#{address[:company]}
|
||||
#{address[:address1]}
|
||||
#{address[:address2]}
|
||||
#{address[:city]} #{address[:state]} #{address[:zip]}
|
||||
#{address[:country]}
|
||||
EOA
|
||||
end
|
||||
end
|
||||
|
||||
def add_payment_method(post, payment_method)
|
||||
post[:member] = escape(payment_method.name, 30)
|
||||
post[:card] = escape(payment_method.number)
|
||||
post[:cvv2] = escape(payment_method.verification_value)
|
||||
post[:expyear] = format(payment_method.year, :four_digits)
|
||||
post[:expmonth] = format(payment_method.month, :two_digits)
|
||||
end
|
||||
|
||||
def add_reference(post, authorization)
|
||||
tranid, member = split_authorization(authorization)
|
||||
post[:transid] = tranid
|
||||
post[:member] = member
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
response = {}
|
||||
|
||||
doc = Nokogiri::XML.fragment(fix_xml(xml))
|
||||
doc.children.each do |node|
|
||||
if node.text?
|
||||
next
|
||||
elsif (node.elements.size == 0)
|
||||
response[node.name.downcase.to_sym] = node.text
|
||||
else
|
||||
node.elements.each do |childnode|
|
||||
name = "#{node.name.downcase}_#{childnode.name.downcase}"
|
||||
response[name.to_sym] = childnode.text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def fix_xml(xml)
|
||||
xml.gsub(/&(?!(?:amp|quot|apos|lt|gt);)/, "&")
|
||||
end
|
||||
|
||||
ACTIONS = {
|
||||
"purchase" => "1",
|
||||
"refund" => "2",
|
||||
"authorize" => "4",
|
||||
"capture" => "5",
|
||||
}
|
||||
|
||||
def commit(action, post)
|
||||
post[:id] = @options[:login]
|
||||
post[:password] = @options[:password]
|
||||
post[:action] = ACTIONS[action] if ACTIONS[action]
|
||||
|
||||
raw = parse(ssl_post(url(action), build_request(post)))
|
||||
|
||||
succeeded = success_from(raw[:result])
|
||||
Response.new(
|
||||
succeeded,
|
||||
message_from(succeeded, raw),
|
||||
raw,
|
||||
:authorization => authorization_from(post, raw),
|
||||
:test => test?
|
||||
)
|
||||
end
|
||||
|
||||
def build_request(post)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
post.each do |field, value|
|
||||
xml.tag!(field, value)
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def url(action)
|
||||
endpoint = "TranPortalXMLServlet"
|
||||
(test? ? test_url : live_url) + endpoint
|
||||
end
|
||||
|
||||
def success_from(result)
|
||||
case result
|
||||
when "CAPTURED", "APPROVED", "NOT ENROLLED", "ENROLLED"
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def message_from(succeeded, response)
|
||||
if succeeded
|
||||
"Succeeded"
|
||||
else
|
||||
(response[:error_text] || response[:result] || "Unable to read error message").split("-").last
|
||||
end
|
||||
end
|
||||
|
||||
def authorization_from(request, response)
|
||||
[response[:tranid], request[:member]].join("|")
|
||||
end
|
||||
|
||||
def split_authorization(authorization)
|
||||
tranid, member = authorization.split("|")
|
||||
[tranid, member]
|
||||
end
|
||||
|
||||
def escape(string, max_length=250)
|
||||
return "" unless string
|
||||
if max_length
|
||||
string = string[0...max_length]
|
||||
end
|
||||
string.gsub(/[^A-Za-z0-9 \-_@\.\n]/, '')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
249
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_base.rb
vendored
Normal file
249
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_base.rb
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
require File.dirname(__FILE__) + '/ideal_response'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# Implementation contains some simplifications
|
||||
# - does not support multiple subID per merchant
|
||||
# - language is fixed to 'nl'
|
||||
class IdealBaseGateway < Gateway
|
||||
class_attribute :server_pem, :pem_password, :default_expiration_period
|
||||
self.default_expiration_period = 'PT10M'
|
||||
self.default_currency = 'EUR'
|
||||
self.pem_password = true
|
||||
|
||||
self.abstract_class = true
|
||||
|
||||
# These constants will never change for most users
|
||||
AUTHENTICATION_TYPE = 'SHA1_RSA'
|
||||
LANGUAGE = 'nl'
|
||||
SUB_ID = '0'
|
||||
API_VERSION = '1.1.0'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :pem)
|
||||
|
||||
options[:pem_password] = options[:password]
|
||||
super
|
||||
end
|
||||
|
||||
# Setup transaction. Get redirect_url from response.service_url
|
||||
def setup_purchase(money, options = {})
|
||||
requires!(options, :issuer_id, :return_url, :order_id, :currency, :description, :entrance_code)
|
||||
|
||||
commit(build_transaction_request(money, options))
|
||||
end
|
||||
|
||||
# Check status of transaction and confirm payment
|
||||
# transaction_id must be a valid transaction_id from a prior setup.
|
||||
def capture(transaction, options = {})
|
||||
options[:transaction_id] = transaction
|
||||
commit(build_status_request(options))
|
||||
end
|
||||
|
||||
# Get list of issuers from response.issuer_list
|
||||
def issuers
|
||||
commit(build_directory_request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def url
|
||||
(test? ? test_url : live_url)
|
||||
end
|
||||
|
||||
def token
|
||||
if @token.nil?
|
||||
@token = create_fingerprint(@options[:pem])
|
||||
end
|
||||
@token
|
||||
end
|
||||
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <AcquirerTrxReq xmlns="http://www.idealdesk.com/Message" version="1.1.0">
|
||||
# <createDateTimeStamp>2001-12-17T09:30:47.0Z</createDateTimeStamp>
|
||||
# <Issuer>
|
||||
# <issuerID>1003</issuerID>
|
||||
# </Issuer>
|
||||
# <Merchant>
|
||||
# <merchantID>000123456</merchantID>
|
||||
# <subID>0</subID>
|
||||
# <authentication>passkey</authentication>
|
||||
# <token>1</token>
|
||||
# <tokenCode>3823ad872eff23</tokenCode>
|
||||
# <merchantReturnURL>https://www.mijnwinkel.nl/betaalafhandeling
|
||||
# </merchantReturnURL>
|
||||
# </Merchant>
|
||||
# <Transaction>
|
||||
# <purchaseID>iDEAL-aankoop 21</purchaseID>
|
||||
# <amount>5999</amount>
|
||||
# <currency>EUR</currency>
|
||||
# <expirationPeriod>PT3M30S</expirationPeriod>
|
||||
# <language>nl</language>
|
||||
# <description>Documentensuite</description>
|
||||
# <entranceCode>D67tyx6rw9IhY71</entranceCode>
|
||||
# </Transaction>
|
||||
# </AcquirerTrxReq>
|
||||
def build_transaction_request(money, options)
|
||||
date_time_stamp = create_time_stamp
|
||||
message = date_time_stamp +
|
||||
options[:issuer_id] +
|
||||
@options[:login] +
|
||||
SUB_ID +
|
||||
options[:return_url] +
|
||||
options[:order_id] +
|
||||
money.to_s +
|
||||
(options[:currency] || currency(money)) +
|
||||
LANGUAGE +
|
||||
options[:description] +
|
||||
options[:entrance_code]
|
||||
token_code = sign_message(@options[:pem], @options[:password], message)
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!
|
||||
xml.tag! 'AcquirerTrxReq', 'xmlns' => 'http://www.idealdesk.com/Message', 'version' => API_VERSION do
|
||||
xml.tag! 'createDateTimeStamp', date_time_stamp
|
||||
xml.tag! 'Issuer' do
|
||||
xml.tag! 'issuerID', options[:issuer_id]
|
||||
end
|
||||
xml.tag! 'Merchant' do
|
||||
xml.tag! 'merchantID', @options[:login]
|
||||
xml.tag! 'subID', SUB_ID
|
||||
xml.tag! 'authentication', AUTHENTICATION_TYPE
|
||||
xml.tag! 'token', token
|
||||
xml.tag! 'tokenCode', token_code
|
||||
xml.tag! 'merchantReturnURL', options[:return_url]
|
||||
end
|
||||
xml.tag! 'Transaction' do
|
||||
xml.tag! 'purchaseID', options[:order_id]
|
||||
xml.tag! 'amount', money
|
||||
xml.tag! 'currency', options[:currency]
|
||||
xml.tag! 'expirationPeriod', options[:expiration_period] || default_expiration_period
|
||||
xml.tag! 'language', LANGUAGE
|
||||
xml.tag! 'description', options[:description]
|
||||
xml.tag! 'entranceCode', options[:entrance_code]
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <AcquirerStatusReq xmlns="http://www.idealdesk.com/Message" version="1.1.0">
|
||||
# <createDateTimeStamp>2001-12-17T09:30:47.0Z</createDateTimeStamp>
|
||||
# <Merchant>
|
||||
# <merchantID>000123456</merchantID>
|
||||
# <subID>0</subID>
|
||||
# <authentication>keyed hash</authentication>
|
||||
# <token>1</token>
|
||||
# <tokenCode>3823ad872eff23</tokenCode>
|
||||
# </Merchant>
|
||||
# <Transaction>
|
||||
# <transactionID>0001023456789112</transactionID>
|
||||
# </Transaction>
|
||||
# </AcquirerStatusReq>
|
||||
def build_status_request(options)
|
||||
datetimestamp = create_time_stamp
|
||||
message = datetimestamp + @options[:login] + SUB_ID + options[:transaction_id]
|
||||
tokenCode = sign_message(@options[:pem], @options[:password], message)
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!
|
||||
xml.tag! 'AcquirerStatusReq', 'xmlns' => 'http://www.idealdesk.com/Message', 'version' => API_VERSION do
|
||||
xml.tag! 'createDateTimeStamp', datetimestamp
|
||||
xml.tag! 'Merchant' do
|
||||
xml.tag! 'merchantID', @options[:login]
|
||||
xml.tag! 'subID', SUB_ID
|
||||
xml.tag! 'authentication' , AUTHENTICATION_TYPE
|
||||
xml.tag! 'token', token
|
||||
xml.tag! 'tokenCode', tokenCode
|
||||
end
|
||||
xml.tag! 'Transaction' do
|
||||
xml.tag! 'transactionID', options[:transaction_id]
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <DirectoryReq xmlns="http://www.idealdesk.com/Message" version="1.1.0">
|
||||
# <createDateTimeStamp>2001-12-17T09:30:47.0Z</createDateTimeStamp>
|
||||
# <Merchant>
|
||||
# <merchantID>000000001</merchantID>
|
||||
# <subID>0</subID>
|
||||
# <authentication>1</authentication>
|
||||
# <token>hashkey</token>
|
||||
# <tokenCode>WajqV1a3nDen0be2r196g9FGFF=</tokenCode>
|
||||
# </Merchant>
|
||||
# </DirectoryReq>
|
||||
def build_directory_request
|
||||
datetimestamp = create_time_stamp
|
||||
message = datetimestamp + @options[:login] + SUB_ID
|
||||
tokenCode = sign_message(@options[:pem], @options[:password], message)
|
||||
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
xml.instruct!
|
||||
xml.tag! 'DirectoryReq', 'xmlns' => 'http://www.idealdesk.com/Message', 'version' => API_VERSION do
|
||||
xml.tag! 'createDateTimeStamp', datetimestamp
|
||||
xml.tag! 'Merchant' do
|
||||
xml.tag! 'merchantID', @options[:login]
|
||||
xml.tag! 'subID', SUB_ID
|
||||
xml.tag! 'authentication', AUTHENTICATION_TYPE
|
||||
xml.tag! 'token', token
|
||||
xml.tag! 'tokenCode', tokenCode
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def commit(request)
|
||||
raw_response = ssl_post(url, request)
|
||||
response = Hash.from_xml(raw_response.to_s)
|
||||
response_type = response.keys[0]
|
||||
|
||||
case response_type
|
||||
when 'AcquirerTrxRes', 'DirectoryRes'
|
||||
success = true
|
||||
when 'ErrorRes'
|
||||
success = false
|
||||
when 'AcquirerStatusRes'
|
||||
raise SecurityError, "Message verification failed.", caller unless status_response_verified?(response)
|
||||
success = (response['AcquirerStatusRes']['Transaction']['status'] == 'Success')
|
||||
else
|
||||
raise ArgumentError, "Unknown response type.", caller
|
||||
end
|
||||
|
||||
return IdealResponse.new(success, response.keys[0], response, :test => test?)
|
||||
end
|
||||
|
||||
def create_fingerprint(cert_file)
|
||||
cert_data = OpenSSL::X509::Certificate.new(cert_file).to_s
|
||||
cert_data = cert_data.sub(/-----BEGIN CERTIFICATE-----/, '')
|
||||
cert_data = cert_data.sub(/-----END CERTIFICATE-----/, '')
|
||||
fingerprint = Base64.decode64(cert_data)
|
||||
fingerprint = Digest::SHA1.hexdigest(fingerprint)
|
||||
return fingerprint.upcase
|
||||
end
|
||||
|
||||
def sign_message(private_key_data, password, data)
|
||||
private_key = OpenSSL::PKey::RSA.new(private_key_data, password)
|
||||
signature = private_key.sign(OpenSSL::Digest::SHA1.new, data.gsub('\s', ''))
|
||||
return Base64.encode64(signature).gsub(/\n/, '')
|
||||
end
|
||||
|
||||
def verify_message(cert_file, data, signature)
|
||||
public_key = OpenSSL::X509::Certificate.new(cert_file).public_key
|
||||
return public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), data)
|
||||
end
|
||||
|
||||
def status_response_verified?(response)
|
||||
transaction = response['AcquirerStatusRes']['Transaction']
|
||||
message = response['AcquirerStatusRes']['createDateTimeStamp'] + transaction['transactionID' ] + transaction['status']
|
||||
message << transaction['consumerAccountNumber'].to_s
|
||||
verify_message(server_pem, message, response['AcquirerStatusRes']['Signature']['signatureValue'])
|
||||
end
|
||||
|
||||
def create_time_stamp
|
||||
Time.now.gmtime.strftime('%Y-%m-%dT%H:%M:%S.000Z')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem
vendored
Executable file
13
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQDCCAakCBELvbPYwDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCREUxDzANBgNVBAgTBkhl
|
||||
c3NlbjESMBAGA1UEBxMJRnJhbmtmdXJ0MQ4wDAYDVQQKEwVpREVBTDEOMAwGA1UECxMFaURFQUwx
|
||||
EzARBgNVBAMTCmlERUFMIFJhYm8wHhcNMDUwODAyMTI1NDE0WhcNMTUwNzMxMTI1NDE0WjBnMQsw
|
||||
CQYDVQQGEwJERTEPMA0GA1UECBMGSGVzc2VuMRIwEAYDVQQHEwlGcmFua2Z1cnQxDjAMBgNVBAoT
|
||||
BWlERUFMMQ4wDAYDVQQLEwVpREVBTDETMBEGA1UEAxMKaURFQUwgUmFibzCBnzANBgkqhkiG9w0B
|
||||
AQEFAAOBjQAwgYkCgYEA486iIKVhr8RNjxH+PZ3yTWx/8k2fqDFm8XU8I1Z5esRmPFnXmlgA8cG7
|
||||
e9AaBPaLoP7Dc8dRQoUO66KMakzGI/WAVdHIJiiKrz8xOcioIgrzPSqec7aqse3J28UraEHkGESJ
|
||||
7dAW7Pw/shrmpmkzKsunLt6AkXss4W3JUndZUN0CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCGy/FK
|
||||
Lotp2ZOTtuLMgvDy74eicq/Znv4bLfpglzAPHycRHcHsXuer/lNHyvpKf2gdYe+IFalUW3OJZWIM
|
||||
jpm4UniJ16RPdgwWVRJEdPr/P7JXMIqD2IEOgujuuTQ7x0VgCf9XrsPsP9ZR5DIPcDDhbrpSE0yF
|
||||
Do77nwG61xMaGA==
|
||||
-----END CERTIFICATE-----
|
||||
29
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_response.rb
vendored
Normal file
29
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal/ideal_response.rb
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class IdealResponse < Response
|
||||
|
||||
def issuer_list
|
||||
list = @params.values[0]['Directory']['Issuer']
|
||||
case list
|
||||
when Hash
|
||||
return [list]
|
||||
when Array
|
||||
return list
|
||||
end
|
||||
end
|
||||
|
||||
def service_url
|
||||
@params.values[0]['Issuer']['issuerAuthenticationURL']
|
||||
end
|
||||
|
||||
def transaction
|
||||
@params.values[0]['Transaction']
|
||||
end
|
||||
|
||||
def error
|
||||
@params.values[0]['Error']
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
66
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal_rabobank.rb
vendored
Normal file
66
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/ideal_rabobank.rb
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
require File.dirname(__FILE__) + '/ideal/ideal_base'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# First, make sure you have everything setup correctly and all of your dependencies in place with:
|
||||
#
|
||||
# require 'rubygems'
|
||||
# require 'active_merchant'
|
||||
#
|
||||
# ActiveMerchant expects the amounts to be given as an Integer in cents. In this case, 10 EUR becomes 1000.
|
||||
#
|
||||
# Create certificates for authentication:
|
||||
#
|
||||
# The PEM file expected should contain both the certificate and the generated PEM file.
|
||||
# Some sample shell commands to generate the certificates:
|
||||
#
|
||||
# openssl genrsa -aes128 -out priv.pem -passout pass:[YOUR PASSWORD] 1024
|
||||
# openssl req -x509 -new -key priv.pem -passin pass:[YOUR PASSWORD] -days 3000 -out cert.cer
|
||||
# cat cert.cer priv.pem > ideal.pem
|
||||
#
|
||||
# Following the steps above, upload cert.cer to the ideal web interface and pass the path of ideal.pem to the :pem option.
|
||||
#
|
||||
# Configure the gateway using your iDEAL bank account info and security settings:
|
||||
#
|
||||
# Create gateway:
|
||||
# gateway = ActiveMerchant::Billing::IdealRabobankGateway.new(
|
||||
# :login => '123456789', # 9 digit merchant number
|
||||
# :pem => File.read(Rails.root + 'config/ideal.pem'),
|
||||
# :password => 'password' # password for the PEM key
|
||||
# )
|
||||
#
|
||||
# Get list of issuers to fill selection list on your payment form:
|
||||
# response = gateway.issuers
|
||||
# list = response.issuer_list
|
||||
#
|
||||
# Request transaction:
|
||||
#
|
||||
# options = {
|
||||
# :issuer_id => '0001',
|
||||
# :expiration_period => 'PT10M',
|
||||
# :return_url => 'http://www.return.url',
|
||||
# :order_id => '1234567890123456',
|
||||
# :currency => 'EUR',
|
||||
# :description => 'Een omschrijving',
|
||||
# :entrance_code => '1234'
|
||||
# }
|
||||
#
|
||||
# response = gateway.setup_purchase(amount, options)
|
||||
# transaction_id = response.transaction['transactionID']
|
||||
# redirect_url = response.service_url
|
||||
#
|
||||
# Mandatory status request will confirm transaction:
|
||||
# response = gateway.capture(transaction_id)
|
||||
#
|
||||
# Implementation contains some simplifications
|
||||
# - does not support multiple subID per merchant
|
||||
# - language is fixed to 'nl'
|
||||
class IdealRabobankGateway < IdealBaseGateway
|
||||
class_attribute :test_url, :live_url
|
||||
|
||||
self.test_url = 'https://idealtest.rabobank.nl/ideal/iDeal'
|
||||
self.live_url = 'https://ideal.rabobank.nl/ideal/iDeal'
|
||||
self.server_pem = File.read(File.dirname(__FILE__) + '/ideal/ideal_rabobank.pem')
|
||||
end
|
||||
end
|
||||
end
|
||||
221
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/inspire.rb
vendored
Normal file
221
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/inspire.rb
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
require File.join(File.dirname(__FILE__), '..', 'check.rb')
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class InspireGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://secure.inspiregateway.net/api/transact.php'
|
||||
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express]
|
||||
self.homepage_url = 'http://www.inspiregateway.com'
|
||||
self.display_name = 'Inspire Commerce'
|
||||
|
||||
# Creates a new InspireGateway
|
||||
#
|
||||
# The gateway requires that a valid login and password be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> -- The Inspire Username.
|
||||
# * <tt>:password</tt> -- The Inspire Passowrd.
|
||||
# See the Inspire Integration Guide for details. (default: +false+)
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
# Pass :store => true in the options to store the
|
||||
# payment info at Inspire Gateway and get a generated
|
||||
# customer_vault_id in the response.
|
||||
# Pass :store => some_number_or_string to specify the
|
||||
# customer_vault_id InspireGateway should use (make sure it's
|
||||
# unique).
|
||||
def authorize(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, creditcard,options)
|
||||
add_address(post, creditcard, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('auth', money, post)
|
||||
end
|
||||
|
||||
def purchase(money, payment_source, options = {})
|
||||
post = {}
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, payment_source, options)
|
||||
add_address(post, payment_source, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('sale', money, post)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
post ={}
|
||||
post[:transactionid] = authorization
|
||||
commit('capture', money, post)
|
||||
end
|
||||
|
||||
def void(authorization, options = {})
|
||||
post ={}
|
||||
post[:transactionid] = authorization
|
||||
commit('void', nil, post)
|
||||
end
|
||||
|
||||
# Update the values (such as CC expiration) stored at
|
||||
# InspireGateway. The CC number must be supplied in the
|
||||
# CreditCard object.
|
||||
def update(vault_id, creditcard, options = {})
|
||||
post = {}
|
||||
post[:customer_vault] = "update_customer"
|
||||
add_customer_vault_id(post, vault_id)
|
||||
add_creditcard(post, creditcard, options)
|
||||
add_address(post, creditcard, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit(nil, nil, post)
|
||||
end
|
||||
|
||||
def delete(vault_id)
|
||||
post = {}
|
||||
post[:customer_vault] = "delete_customer"
|
||||
add_customer_vault_id(post, vault_id)
|
||||
commit(nil, nil, post)
|
||||
end
|
||||
|
||||
# To match the other stored-value gateways, like TrustCommerce,
|
||||
# store and unstore need to be defined
|
||||
def store(creditcard, options = {})
|
||||
billing_id = options.delete(:billing_id).to_s || true
|
||||
authorize(100, creditcard, options.merge(:store => billing_id))
|
||||
end
|
||||
|
||||
alias_method :unstore, :delete
|
||||
|
||||
private
|
||||
def add_customer_data(post, options)
|
||||
if options.has_key? :email
|
||||
post[:email] = options[:email]
|
||||
end
|
||||
|
||||
if options.has_key? :ip
|
||||
post[:ipaddress] = options[:ip]
|
||||
end
|
||||
end
|
||||
|
||||
def add_address(post, creditcard, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:address1] = address[:address1].to_s
|
||||
post[:address2] = address[:address2].to_s unless address[:address2].blank?
|
||||
post[:company] = address[:company].to_s
|
||||
post[:phone] = address[:phone].to_s
|
||||
post[:zip] = address[:zip].to_s
|
||||
post[:city] = address[:city].to_s
|
||||
post[:country] = address[:country].to_s
|
||||
post[:state] = address[:state].blank? ? 'n/a' : address[:state]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:orderid] = options[:order_id].to_s.gsub(/[^\w.]/, '')
|
||||
post[:orderdescription] = options[:description]
|
||||
end
|
||||
|
||||
def add_payment_source(params, source, options={})
|
||||
case determine_funding_source(source)
|
||||
when :vault then add_customer_vault_id(params, source)
|
||||
when :credit_card then add_creditcard(params, source, options)
|
||||
when :check then add_check(params, source)
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_vault_id(params,vault_id)
|
||||
params[:customer_vault_id] = vault_id
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard,options)
|
||||
if options[:store]
|
||||
post[:customer_vault] = "add_customer"
|
||||
post[:customer_vault_id] = options[:store] unless options[:store] == true
|
||||
end
|
||||
post[:ccnumber] = creditcard.number
|
||||
post[:cvv] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:ccexp] = expdate(creditcard)
|
||||
post[:firstname] = creditcard.first_name
|
||||
post[:lastname] = creditcard.last_name
|
||||
end
|
||||
|
||||
def add_check(post, check)
|
||||
post[:payment] = 'check' # Set transaction to ACH
|
||||
post[:checkname] = check.name # The name on the customer's Checking Account
|
||||
post[:checkaba] = check.routing_number # The customer's bank routing number
|
||||
post[:checkaccount] = check.account_number # The customer's account number
|
||||
post[:account_holder_type] = check.account_holder_type # The customer's type of ACH account
|
||||
post[:account_type] = check.account_type # The customer's type of ACH account
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
results = {}
|
||||
body.split(/&/).each do |pair|
|
||||
key,val = pair.split(/=/)
|
||||
results[key] = val
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def commit(action, money, parameters)
|
||||
parameters[:amount] = amount(money) if money
|
||||
|
||||
response = parse( ssl_post(self.live_url, post_data(action,parameters)) )
|
||||
|
||||
Response.new(response["response"] == "1", message_from(response), response,
|
||||
:authorization => response["transactionid"],
|
||||
:test => test?,
|
||||
:cvv_result => response["cvvresponse"],
|
||||
:avs_result => { :code => response["avsresponse"] }
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = sprintf("%.4i", creditcard.year)
|
||||
month = sprintf("%.2i", creditcard.month)
|
||||
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
|
||||
def message_from(response)
|
||||
case response["responsetext"]
|
||||
when "SUCCESS","Approved"
|
||||
"This transaction has been approved"
|
||||
when "DECLINE"
|
||||
"This transaction has been declined"
|
||||
else
|
||||
response["responsetext"]
|
||||
end
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {}
|
||||
post[:username] = @options[:login]
|
||||
post[:password] = @options[:password]
|
||||
post[:type] = action if action
|
||||
|
||||
request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
|
||||
request
|
||||
end
|
||||
|
||||
def determine_funding_source(source)
|
||||
case
|
||||
when source.is_a?(String) then :vault
|
||||
when CreditCard.card_companies.keys.include?(card_brand(source)) then :credit_card
|
||||
when card_brand(source) == 'check' then :check
|
||||
else raise ArgumentError, "Unsupported funding source provided"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
163
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/instapay.rb
vendored
Executable file
163
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/instapay.rb
vendored
Executable file
@@ -0,0 +1,163 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class InstapayGateway < Gateway
|
||||
self.live_url = 'https://trans.instapaygateway.com/cgi-bin/process.cgi'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
self.money_format = :dollars
|
||||
self.default_currency = 'USD'
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.instapayllc.com'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'InstaPay'
|
||||
|
||||
SUCCESS = "Accepted"
|
||||
SUCCESS_MESSAGE = "The transaction has been approved"
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard, options = {})
|
||||
post = {}
|
||||
post[:authonly] = 1
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('ns_quicksale_cc', post)
|
||||
end
|
||||
|
||||
def purchase(money, creditcard, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_invoice(post, options)
|
||||
add_creditcard(post, creditcard)
|
||||
add_address(post, options)
|
||||
add_customer_data(post, options)
|
||||
|
||||
commit('ns_quicksale_cc', post)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
post = {}
|
||||
add_amount(post, money)
|
||||
add_reference(post, authorization)
|
||||
commit('ns_quicksale_cc', post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_amount(post, money)
|
||||
post[:amount] = amount(money)
|
||||
end
|
||||
|
||||
def add_reference(post, reference)
|
||||
post[:postonly] = reference
|
||||
end
|
||||
|
||||
def add_customer_data(post, options)
|
||||
post[:ci_email] = options[:email]
|
||||
post["ci_IP Address"] = options[:ip]
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:ci_billaddr1] = address[:address1]
|
||||
post[:ci_billaddr2] = address[:address2]
|
||||
post[:ci_billcity] = address[:city]
|
||||
post[:ci_billstate] = address[:state]
|
||||
post[:ci_billzip] = address[:zip]
|
||||
post[:ci_billcountry] = address[:country]
|
||||
post[:ci_phone] = address[:phone]
|
||||
end
|
||||
|
||||
if address = options[:shipping_address]
|
||||
post[:ci_shipaddr1] = address[:address1]
|
||||
post[:ci_shipaddr2] = address[:address2]
|
||||
post[:ci_shipcity] = address[:city]
|
||||
post[:ci_shipstate] = address[:state]
|
||||
post[:ci_shipzip] = address[:zip]
|
||||
post[:ci_shipcountry] = address[:country]
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
post[:merchantordernumber] = options[:order_id]
|
||||
post[:ci_memo] = options[:description]
|
||||
post[:pocustomerrefid] = options[:invoice]
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post[:ccnum] = creditcard.number
|
||||
post[:expmon] = format(creditcard.month, :two_digits)
|
||||
post[:cvv2] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:expyear] = creditcard.year
|
||||
post[:ccname] = creditcard.name
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
results = {}
|
||||
fields = body.split("\r\n")
|
||||
|
||||
response = fields[1].split('=')
|
||||
response_data = response[1].split(':')
|
||||
|
||||
if response[0] == SUCCESS
|
||||
results[:success] = true
|
||||
results[:message] = SUCCESS_MESSAGE
|
||||
results[:transaction_type] = response_data[0]
|
||||
results[:authorization_code] = response_data[1]
|
||||
results[:reference_number] = response_data[2]
|
||||
results[:batch_number] = response_data[3]
|
||||
results[:transaction_id] = response_data[4]
|
||||
results[:avs_result] = response_data[5]
|
||||
results[:authorize_net] = response_data[6]
|
||||
results[:cvv_result] = response_data[7]
|
||||
else
|
||||
results[:success] = false
|
||||
results[:result] = response_data[0]
|
||||
results[:response_code] = response_data[1]
|
||||
results[:message] = response_data[2]
|
||||
end
|
||||
|
||||
fields[1..-1].each do |pair|
|
||||
key, value = pair.split('=')
|
||||
results[key] = value
|
||||
end
|
||||
results
|
||||
end
|
||||
|
||||
def commit(action, parameters)
|
||||
data = ssl_post self.live_url, post_data(action, parameters)
|
||||
response = parse(data)
|
||||
|
||||
Response.new(response[:success] , response[:message], response,
|
||||
:authorization => response[:transaction_id],
|
||||
:avs_result => { :code => response[:avs_result] },
|
||||
:cvv_result => response[:cvv_result]
|
||||
)
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {}
|
||||
post[:acctid] = @options[:login]
|
||||
if(@options[:password])
|
||||
post[:merchantpin] = @options[:password]
|
||||
end
|
||||
post[:action] = action
|
||||
request = post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
||||
request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
262
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/iridium.rb
vendored
Normal file
262
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/iridium.rb
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# For more information on the Iridium Gateway please download the
|
||||
# documentation from their Merchant Management System.
|
||||
#
|
||||
# The login and password are not the username and password you use to
|
||||
# login to the Iridium Merchant Management System. Instead, you will
|
||||
# use the API username and password you were issued separately.
|
||||
class IridiumGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://gw1.iridiumcorp.net/'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['GB', 'ES']
|
||||
self.default_currency = 'EUR'
|
||||
self.money_format = :cents
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :maestro, :jcb, :solo, :diners_club]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.iridiumcorp.co.uk/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Iridium'
|
||||
|
||||
CURRENCY_CODES = {
|
||||
"AUD" => '036',
|
||||
"CAD" => '124',
|
||||
"EUR" => '978',
|
||||
"GBP" => '826',
|
||||
"MXN" => '484',
|
||||
"NZD" => '554',
|
||||
"USD" => '840',
|
||||
}
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, payment_source, options = {})
|
||||
setup_address_hash(options)
|
||||
|
||||
if payment_source.respond_to?(:number)
|
||||
commit(build_purchase_request('PREAUTH', money, payment_source, options), options)
|
||||
else
|
||||
commit(build_reference_request('PREAUTH', money, payment_source, options), options)
|
||||
end
|
||||
end
|
||||
|
||||
def purchase(money, payment_source, options = {})
|
||||
setup_address_hash(options)
|
||||
|
||||
if payment_source.respond_to?(:number)
|
||||
commit(build_purchase_request('SALE', money, payment_source, options), options)
|
||||
else
|
||||
commit(build_reference_request('SALE', money, payment_source, options), options)
|
||||
end
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
commit(build_reference_request('COLLECTION', money, authorization, options), options)
|
||||
end
|
||||
|
||||
def credit(money, authorization, options={})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, authorization, options)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options={})
|
||||
commit(build_reference_request('REFUND', money, authorization, options), options)
|
||||
end
|
||||
|
||||
def void(authorization, options={})
|
||||
commit(build_reference_request('VOID', nil, authorization, options), options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_purchase_request(type, money, creditcard, options)
|
||||
options.merge!(:action => 'CardDetailsTransaction')
|
||||
build_request(options) do |xml|
|
||||
add_purchase_data(xml, type, money, options)
|
||||
add_creditcard(xml, creditcard)
|
||||
add_customerdetails(xml, creditcard, options[:billing_address], options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_reference_request(type, money, authorization, options)
|
||||
options.merge!(:action => 'CrossReferenceTransaction')
|
||||
order_id, cross_reference, auth_id = authorization.split(";")
|
||||
build_request(options) do |xml|
|
||||
if money
|
||||
details = {'CurrencyCode' => currency_code(options[:currency] || default_currency), 'Amount' => amount(money)}
|
||||
else
|
||||
details = {'CurrencyCode' => currency_code(default_currency), 'Amount' => '0'}
|
||||
end
|
||||
xml.tag! 'TransactionDetails', details do
|
||||
xml.tag! 'MessageDetails', {'TransactionType' => type, 'CrossReference' => cross_reference}
|
||||
xml.tag! 'OrderID', (options[:order_id] || order_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_request(options)
|
||||
requires!(options, :action)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
||||
xml.tag! 'soap:Envelope', { 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
|
||||
xml.tag! 'soap:Body' do
|
||||
xml.tag! options[:action], {'xmlns' => "https://www.thepaymentgateway.net/"} do
|
||||
xml.tag! 'PaymentMessage' do
|
||||
add_merchant_data(xml, options)
|
||||
yield(xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def setup_address_hash(options)
|
||||
options[:billing_address] = options[:billing_address] || options[:address] || {}
|
||||
options[:shipping_address] = options[:shipping_address] || {}
|
||||
end
|
||||
|
||||
def add_purchase_data(xml, type, money, options)
|
||||
requires!(options, :order_id)
|
||||
xml.tag! 'TransactionDetails', {'Amount' => amount(money), 'CurrencyCode' => currency_code(options[:currency] || currency(money))} do
|
||||
xml.tag! 'MessageDetails', {'TransactionType' => type}
|
||||
xml.tag! 'OrderID', options[:order_id]
|
||||
xml.tag! 'TransactionControl' do
|
||||
xml.tag! 'ThreeDSecureOverridePolicy', 'FALSE'
|
||||
xml.tag! 'EchoAVSCheckResult', 'TRUE'
|
||||
xml.tag! 'EchoCV2CheckResult', 'TRUE'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_customerdetails(xml, creditcard, address, options, shipTo = false)
|
||||
xml.tag! 'CustomerDetails' do
|
||||
if address
|
||||
unless address[:country].blank?
|
||||
country_code = Country.find(address[:country]).code(:numeric)
|
||||
end
|
||||
xml.tag! 'BillingAddress' do
|
||||
xml.tag! 'Address1', address[:address1]
|
||||
xml.tag! 'Address2', address[:address2]
|
||||
xml.tag! 'City', address[:city]
|
||||
xml.tag! 'State', address[:state]
|
||||
xml.tag! 'PostCode', address[:zip]
|
||||
xml.tag! 'CountryCode', country_code if country_code
|
||||
end
|
||||
xml.tag! 'PhoneNumber', address[:phone]
|
||||
end
|
||||
|
||||
xml.tag! 'EmailAddress', options[:email]
|
||||
xml.tag! 'CustomerIPAddress', options[:ip] || "127.0.0.1"
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(xml, creditcard)
|
||||
xml.tag! 'CardDetails' do
|
||||
xml.tag! 'CardName', creditcard.name
|
||||
xml.tag! 'CV2', creditcard.verification_value if creditcard.verification_value
|
||||
xml.tag! 'CardNumber', creditcard.number
|
||||
xml.tag! 'ExpiryDate', { 'Month' => creditcard.month.to_s.rjust(2, "0"), 'Year' => creditcard.year.to_s[/\d\d$/] }
|
||||
end
|
||||
end
|
||||
|
||||
def add_merchant_data(xml, options)
|
||||
xml.tag! 'MerchantAuthentication', {"MerchantID" => @options[:login], "Password" => @options[:password]}
|
||||
end
|
||||
|
||||
def commit(request, options)
|
||||
requires!(options, :action)
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, request,
|
||||
{"SOAPAction" => "https://www.thepaymentgateway.net/#{options[:action]}",
|
||||
"Content-Type" => "text/xml; charset=utf-8" }))
|
||||
|
||||
success = response[:transaction_result][:status_code] == "0"
|
||||
message = response[:transaction_result][:message]
|
||||
authorization = success ? [ options[:order_id], response[:transaction_output_data][:cross_reference], response[:transaction_output_data][:auth_code] ].compact.join(";") : nil
|
||||
|
||||
Response.new(success, message, response,
|
||||
:test => test?,
|
||||
:authorization => authorization)
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
reply = {}
|
||||
xml = REXML::Document.new(xml)
|
||||
if (root = REXML::XPath.first(xml, "//CardDetailsTransactionResponse")) or
|
||||
(root = REXML::XPath.first(xml, "//CrossReferenceTransactionResponse"))
|
||||
root.elements.to_a.each do |node|
|
||||
case node.name
|
||||
when 'Message'
|
||||
reply[:message] = reply(node.text)
|
||||
else
|
||||
parse_element(reply, node)
|
||||
end
|
||||
end
|
||||
elsif root = REXML::XPath.first(xml, "//soap:Fault")
|
||||
parse_element(reply, root)
|
||||
reply[:message] = "#{reply[:faultcode]}: #{reply[:faultstring]}"
|
||||
end
|
||||
reply
|
||||
end
|
||||
|
||||
def parse_element(reply, node)
|
||||
case node.name
|
||||
when "CrossReferenceTransactionResult"
|
||||
reply[:transaction_result] = {}
|
||||
node.attributes.each do |a,b|
|
||||
reply[:transaction_result][a.underscore.to_sym] = b
|
||||
end
|
||||
node.elements.each{|e| parse_element(reply[:transaction_result], e) } if node.has_elements?
|
||||
|
||||
when "CardDetailsTransactionResult"
|
||||
reply[:transaction_result] = {}
|
||||
node.attributes.each do |a,b|
|
||||
reply[:transaction_result][a.underscore.to_sym] = b
|
||||
end
|
||||
node.elements.each{|e| parse_element(reply[:transaction_result], e) } if node.has_elements?
|
||||
|
||||
when "TransactionOutputData"
|
||||
reply[:transaction_output_data] = {}
|
||||
node.attributes.each{|a,b| reply[:transaction_output_data][a.underscore.to_sym] = b }
|
||||
node.elements.each{|e| parse_element(reply[:transaction_output_data], e) } if node.has_elements?
|
||||
when "CustomVariables"
|
||||
reply[:custom_variables] = {}
|
||||
node.attributes.each{|a,b| reply[:custom_variables][a.underscore.to_sym] = b }
|
||||
node.elements.each{|e| parse_element(reply[:custom_variables], e) } if node.has_elements?
|
||||
when "GatewayEntryPoints"
|
||||
reply[:gateway_entry_points] = {}
|
||||
node.attributes.each{|a,b| reply[:gateway_entry_points][a.underscore.to_sym] = b }
|
||||
node.elements.each{|e| parse_element(reply[:gateway_entry_points], e) } if node.has_elements?
|
||||
else
|
||||
k = node.name.underscore.to_sym
|
||||
if node.has_elements?
|
||||
reply[k] = {}
|
||||
node.elements.each{|e| parse_element(reply[k], e) }
|
||||
else
|
||||
if node.has_attributes?
|
||||
reply[k] = {}
|
||||
node.attributes.each{|a,b| reply[k][a.underscore.to_sym] = b }
|
||||
else
|
||||
reply[k] = node.text
|
||||
end
|
||||
end
|
||||
end
|
||||
reply
|
||||
end
|
||||
|
||||
def currency_code(currency)
|
||||
CURRENCY_CODES[currency]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
448
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/itransact.rb
vendored
Normal file
448
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/itransact.rb
vendored
Normal file
@@ -0,0 +1,448 @@
|
||||
require 'nokogiri'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
# iTransact, Inc. is an authorized reseller of the PaymentClearing gateway. If your merchant service provider uses PaymentClearing.com to process payments, you can use this module.
|
||||
#
|
||||
#
|
||||
# Please note, the username and API Access Key are not what you use to log into the Merchant Control Panel.
|
||||
#
|
||||
# ==== How to get your GatewayID and API Access Key
|
||||
#
|
||||
# 1. If you don't already have a Gateway Account, go to http://www.itransact.com/merchant/test.html to sign up.
|
||||
# 2. Go to http://support.paymentclearing.com and login or register, if necessary.
|
||||
# 3. Click on "Submit a Ticket."
|
||||
# 4. Select "Merchant Support" as the department and click "Next"
|
||||
# 5. Enter *both* your company name and GatewayID. Put "API Access Key" in the subject. In the body, you can request a username, but it may already be in use.
|
||||
#
|
||||
# ==== Initialization
|
||||
#
|
||||
# Once you have the username, API Access Key, and your GatewayId, you're ready
|
||||
# to begin. You initialize the Gateway like so:
|
||||
#
|
||||
# gateway = ActiveMerchant::Billing::ItransactGateway.new(
|
||||
# :login => "#{THE_USERNAME}",
|
||||
# :password => "#{THE_API_ACCESS_KEY}",
|
||||
# :gateway_id => "#{THE_GATEWAY_ID}"
|
||||
# )
|
||||
#
|
||||
# ==== Important Notes
|
||||
# 1. Recurring is not implemented
|
||||
# 1. CreditTransactions are not implemented (these are credits not related to a previously run transaction).
|
||||
# 1. TransactionStatus is not implemented
|
||||
#
|
||||
class ItransactGateway < Gateway
|
||||
self.live_url = self.test_url = 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans2.cgi'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.itransact.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'iTransact'
|
||||
|
||||
#
|
||||
# Creates a new instance of the iTransact Gateway.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>options</tt> - A Hash of options
|
||||
#
|
||||
# ==== Options Hash
|
||||
# * <tt>:login</tt> - A String containing your PaymentClearing assigned API Access Username
|
||||
# * <tt>:password</tt> - A String containing your PaymentClearing assigned API Access Key
|
||||
# * <tt>:gateway_id</tt> - A String containing your PaymentClearing assigned GatewayID
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Run *all* transactions with the 'TestMode' element set to 'TRUE'.
|
||||
#
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :gateway_id)
|
||||
super
|
||||
end
|
||||
|
||||
# Performs an authorize transaction. In PaymentClearing's documentation
|
||||
# this is known as a "PreAuth" transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents.
|
||||
# * <tt>creditcard</tt> - The CreditCard details for the transaction
|
||||
# * <tt>options</tt> - A Hash of options
|
||||
#
|
||||
# ==== Options Hash
|
||||
# The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
|
||||
# * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
|
||||
# * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
|
||||
# * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
|
||||
#
|
||||
# ==== Examples
|
||||
# response = gateway.authorize(1000, creditcard,
|
||||
# :order_id => '1212', :address => {...}, :email => 'test@test.com',
|
||||
# :order_items => [
|
||||
# {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
|
||||
# {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
|
||||
# ],
|
||||
# :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
|
||||
# :send_customer_email => true,
|
||||
# :send_merchant_email => true,
|
||||
# :email_text => ['line1', 'line2', 'line3'],
|
||||
# :test_mode => true
|
||||
# )
|
||||
#
|
||||
def authorize(money, payment_source, options = {})
|
||||
payload = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.AuthTransaction {
|
||||
xml.Preauth
|
||||
add_customer_data(xml, payment_source, options)
|
||||
add_invoice(xml, money, options)
|
||||
add_payment_source(xml, payment_source)
|
||||
add_transaction_control(xml, options)
|
||||
add_vendor_data(xml, options)
|
||||
}
|
||||
end.doc
|
||||
|
||||
commit(payload)
|
||||
end
|
||||
|
||||
# Performs an authorize and capture in single transaction. In PaymentClearing's
|
||||
# documentation this is known as an "Auth" or a "Sale" transaction
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be captured. Should be <tt>nil</tt> or an Integer amount in cents.
|
||||
# * <tt>creditcard</tt> - The CreditCard details for the transaction
|
||||
# * <tt>options</tt> - A Hash of options
|
||||
#
|
||||
# ==== Options Hash
|
||||
# The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
|
||||
# * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
|
||||
# * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
|
||||
# * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
|
||||
#
|
||||
# ==== Examples
|
||||
# response = gateway.purchase(1000, creditcard,
|
||||
# :order_id => '1212', :address => {...}, :email => 'test@test.com',
|
||||
# :order_items => [
|
||||
# {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
|
||||
# {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
|
||||
# ],
|
||||
# :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
|
||||
# :send_customer_email => true,
|
||||
# :send_merchant_email => true,
|
||||
# :email_text => ['line1', 'line2', 'line3'],
|
||||
# :test_mode => true
|
||||
# )
|
||||
#
|
||||
def purchase(money, payment_source, options = {})
|
||||
payload = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.AuthTransaction {
|
||||
add_customer_data(xml, payment_source, options)
|
||||
add_invoice(xml, money, options)
|
||||
add_payment_source(xml, payment_source)
|
||||
add_transaction_control(xml, options)
|
||||
add_vendor_data(xml, options)
|
||||
}
|
||||
end.doc
|
||||
|
||||
commit(payload)
|
||||
end
|
||||
|
||||
# Captures the funds from an authorize transaction. In PaymentClearing's
|
||||
# documentation this is known as a "PostAuth" transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
|
||||
# * <tt>options</tt> - A Hash of options, all are optional.
|
||||
#
|
||||
# ==== Options Hash
|
||||
# The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
|
||||
# * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
|
||||
# * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
|
||||
#
|
||||
# ==== Examples
|
||||
# response = gateway.capture(1000, creditcard,
|
||||
# :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
|
||||
# :send_customer_email => true,
|
||||
# :send_merchant_email => true,
|
||||
# :email_text => ['line1', 'line2', 'line3'],
|
||||
# :test_mode => true
|
||||
# )
|
||||
#
|
||||
def capture(money, authorization, options = {})
|
||||
payload = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.PostAuthTransaction {
|
||||
xml.OperationXID(authorization)
|
||||
add_invoice(xml, money, options)
|
||||
add_transaction_control(xml, options)
|
||||
add_vendor_data(xml, options)
|
||||
}
|
||||
end.doc
|
||||
|
||||
commit(payload)
|
||||
end
|
||||
|
||||
# This will reverse a previously run transaction which *has* *not* settled.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
|
||||
# * <tt>options</tt> - A Hash of options, all are optional
|
||||
#
|
||||
# ==== Options Hash
|
||||
# The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
|
||||
# * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
|
||||
# * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
|
||||
#
|
||||
# ==== Examples
|
||||
# response = gateway.void('9999999999',
|
||||
# :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
|
||||
# :send_customer_email => true,
|
||||
# :send_merchant_email => true,
|
||||
# :email_text => ['line1', 'line2', 'line3'],
|
||||
# :test_mode => true
|
||||
# )
|
||||
#
|
||||
def void(authorization, options = {})
|
||||
payload = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.VoidTransaction {
|
||||
xml.OperationXID(authorization)
|
||||
add_transaction_control(xml, options)
|
||||
add_vendor_data(xml, options)
|
||||
}
|
||||
end.doc
|
||||
|
||||
commit(payload)
|
||||
end
|
||||
|
||||
# This will reverse a previously run transaction which *has* settled.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be credited. Should be an Integer amount in cents
|
||||
# * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
|
||||
# * <tt>options</tt> - A Hash of options, all are optional
|
||||
#
|
||||
# ==== Options Hash
|
||||
# The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
|
||||
# * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
|
||||
# * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
|
||||
# * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
|
||||
# * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
|
||||
#
|
||||
# ==== Examples
|
||||
# response = gateway.refund(555, '9999999999',
|
||||
# :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
|
||||
# :send_customer_email => true,
|
||||
# :send_merchant_email => true,
|
||||
# :email_text => ['line1', 'line2', 'line3'],
|
||||
# :test_mode => true
|
||||
# )
|
||||
#
|
||||
def refund(money, authorization, options = {})
|
||||
payload = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.TranCredTransaction {
|
||||
xml.OperationXID(authorization)
|
||||
add_invoice(xml, money, options)
|
||||
add_transaction_control(xml, options)
|
||||
add_vendor_data(xml, options)
|
||||
}
|
||||
end.doc
|
||||
|
||||
commit(payload)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_customer_data(xml, payment_source, options)
|
||||
billing_address = options[:billing_address] || options[:address]
|
||||
shipping_address = options[:shipping_address] || options[:address]
|
||||
|
||||
xml.CustomerData {
|
||||
xml.Email(options[:email]) unless options[:email].blank?
|
||||
xml.CustId(options[:order_id]) unless options[:order_id].blank?
|
||||
xml.BillingAddress {
|
||||
xml.FirstName(payment_source.first_name || parse_first_name(billing_address[:name]))
|
||||
xml.LastName(payment_source.last_name || parse_last_name(billing_address[:name]))
|
||||
xml.Address1(billing_address[:address1])
|
||||
xml.Address2(billing_address[:address2]) unless billing_address[:address2].blank?
|
||||
xml.City(billing_address[:city])
|
||||
xml.State(billing_address[:state])
|
||||
xml.Zip(billing_address[:zip])
|
||||
xml.Country(billing_address[:country])
|
||||
xml.Phone(billing_address[:phone])
|
||||
}
|
||||
xml.ShippingAddress {
|
||||
xml.FirstName(payment_source.first_name || parse_first_name(shipping_address[:name]))
|
||||
xml.LastName(payment_source.last_name || parse_last_name(shipping_address[:name]))
|
||||
xml.Address1(shipping_address[:address1])
|
||||
xml.Address2(shipping_address[:address2]) unless shipping_address[:address2].blank?
|
||||
xml.City(shipping_address[:city])
|
||||
xml.State(shipping_address[:state])
|
||||
xml.Zip(shipping_address[:zip])
|
||||
xml.Country(shipping_address[:country])
|
||||
xml.Phone(shipping_address[:phone])
|
||||
} unless shipping_address.blank?
|
||||
}
|
||||
end
|
||||
|
||||
def add_invoice(xml, money, options)
|
||||
xml.AuthCode options[:force] if options[:force]
|
||||
if options[:order_items].blank?
|
||||
xml.Total(amount(money)) unless(money.nil? || money < 0.01)
|
||||
xml.Description(options[:description]) unless( options[:description].blank?)
|
||||
else
|
||||
xml.OrderItems {
|
||||
options[:order_items].each do |item|
|
||||
xml.Item {
|
||||
xml.Description(item[:description])
|
||||
xml.Cost(amount(item[:cost]))
|
||||
xml.Qty(item[:quantity].to_s)
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def add_payment_source(xml, source)
|
||||
case determine_funding_source(source)
|
||||
when :credit_card then add_creditcard(xml, source)
|
||||
when :check then add_check(xml, source)
|
||||
end
|
||||
end
|
||||
|
||||
def determine_funding_source(payment_source)
|
||||
case payment_source
|
||||
when ActiveMerchant::Billing::CreditCard
|
||||
:credit_card
|
||||
when ActiveMerchant::Billing::Check
|
||||
:check
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(xml, creditcard)
|
||||
xml.AccountInfo {
|
||||
xml.CardAccount {
|
||||
xml.AccountNumber(creditcard.number.to_s)
|
||||
xml.ExpirationMonth(creditcard.month.to_s.rjust(2,'0'))
|
||||
xml.ExpirationYear(creditcard.year.to_s)
|
||||
xml.CVVNumber(creditcard.verification_value.to_s) unless creditcard.verification_value.blank?
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def add_check(xml, check)
|
||||
xml.AccountInfo {
|
||||
xml.ABA(check.routing_number.to_s)
|
||||
xml.AccountNumber(check.account_number.to_s)
|
||||
xml.AccountSource(check.account_type.to_s)
|
||||
xml.AccountType(check.account_holder_type.to_s)
|
||||
xml.CheckNumber(check.number.to_s)
|
||||
}
|
||||
end
|
||||
|
||||
def add_transaction_control(xml, options)
|
||||
xml.TransactionControl {
|
||||
# if there was a 'global' option set...
|
||||
xml.TestMode(@options[:test_mode].upcase) if !@options[:test_mode].blank?
|
||||
# allow the global option to be overridden...
|
||||
xml.TestMode(options[:test_mode].upcase) if !options[:test_mode].blank?
|
||||
xml.SendCustomerEmail(options[:send_customer_email].upcase) unless options[:send_customer_email].blank?
|
||||
xml.SendMerchantEmail(options[:send_merchant_email].upcase) unless options[:send_merchant_email].blank?
|
||||
xml.EmailText {
|
||||
options[:email_text].each do |item|
|
||||
xml.EmailTextItem(item)
|
||||
end
|
||||
} if options[:email_text]
|
||||
}
|
||||
end
|
||||
|
||||
def add_vendor_data(xml, options)
|
||||
return if options[:vendor_data].blank?
|
||||
xml.VendorData {
|
||||
options[:vendor_data].each do |k,v|
|
||||
xml.Element {
|
||||
xml.Name(k)
|
||||
xml.Key(v)
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def commit(payload)
|
||||
# Set the Content-Type header -- otherwise the URL decoding messes up
|
||||
# the Base64 encoded payload signature!
|
||||
response = parse(ssl_post(self.live_url, post_data(payload), 'Content-Type' => 'text/xml'))
|
||||
|
||||
Response.new(successful?(response), response[:error_message], response,
|
||||
:test => test?,
|
||||
:authorization => response[:xid],
|
||||
:avs_result => { :code => response[:avs_response] },
|
||||
:cvv_result => response[:cvv_response])
|
||||
end
|
||||
|
||||
def post_data(payload)
|
||||
payload_xml = payload.root.to_xml(:indent => 0)
|
||||
|
||||
payload_signature = sign_payload(payload_xml)
|
||||
|
||||
request = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.GatewayInterface {
|
||||
xml.APICredentials {
|
||||
xml.Username(@options[:login])
|
||||
xml.PayloadSignature(payload_signature)
|
||||
xml.TargetGateway(@options[:gateway_id])
|
||||
}
|
||||
}
|
||||
end.doc
|
||||
|
||||
request.root.children.first.after payload.root
|
||||
request.to_xml(:indent => 0)
|
||||
end
|
||||
|
||||
def parse(raw_xml)
|
||||
doc = REXML::Document.new(raw_xml)
|
||||
response = Hash.new
|
||||
transaction_result = doc.root.get_elements('TransactionResponse/TransactionResult/*')
|
||||
transaction_result.each do |e|
|
||||
response[e.name.to_s.underscore.to_sym] = e.text unless e.text.blank?
|
||||
end
|
||||
response
|
||||
end
|
||||
|
||||
def successful?(response)
|
||||
# Turns out the PaymentClearing gateway is not consistent...
|
||||
response[:status].downcase =='ok'
|
||||
end
|
||||
|
||||
def test_mode?(response)
|
||||
# The '1' is a legacy thing; most of the time it should be 'TRUE'...
|
||||
response[:test_mode] == 'TRUE' || response[:test_mode] == '1'
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
response[:error_message]
|
||||
end
|
||||
|
||||
def sign_payload(payload)
|
||||
key = @options[:password].to_s
|
||||
digest=OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new(key), key, payload)
|
||||
signature = Base64.encode64(digest)
|
||||
signature.chomp!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
275
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/jetpay.rb
vendored
Normal file
275
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/jetpay.rb
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class JetpayGateway < Gateway
|
||||
self.test_url = 'https://test1.jetpay.com/jetpay'
|
||||
self.live_url = 'https://gateway17.jetpay.com/jetpay'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.jetpay.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'JetPay'
|
||||
|
||||
# all transactions are in cents
|
||||
self.money_format = :cents
|
||||
|
||||
ACTION_CODE_MESSAGES = {
|
||||
"001" => "Refer to card issuer.",
|
||||
"002" => "Refer to card issuer, special condition.",
|
||||
"003" => "Pick up card.",
|
||||
"200" => "Deny - Pick up card.",
|
||||
"005" => "Do not honor.",
|
||||
"100" => "Deny.",
|
||||
"006" => "Error.",
|
||||
"181" => "Format error.",
|
||||
"007" => "Pickup card, special condition.",
|
||||
"104" => "Deny - New card issued.",
|
||||
"110" => "Invalid amount.",
|
||||
"014" => "Invalid account number (no such number).",
|
||||
"111" => "Invalid account.",
|
||||
"015" => "No such issuer.",
|
||||
"103" => "Deny - Invalid manual Entry 4DBC.",
|
||||
"182" => "Please wait.",
|
||||
"109" => "Invalid merchant.",
|
||||
"041" => "Pick up card (lost card).",
|
||||
"043" => "Pick up card (stolen card).",
|
||||
"051" => "Insufficient funds.",
|
||||
"052" => "No checking account.",
|
||||
"105" => "Deny - Account Cancelled.",
|
||||
"054" => "Expired Card.",
|
||||
"101" => "Expired Card.",
|
||||
"183" => "Invalid currency code.",
|
||||
"057" => "Transaction not permitted to cardholder.",
|
||||
"115" => "Service not permitted.",
|
||||
"062" => "Restricted card.",
|
||||
"189" => "Deny - Cancelled or Closed Merchant/SE.",
|
||||
"188" => "Deny - Expiration date required.",
|
||||
"125" => "Invalid effective date.",
|
||||
"122" => "Invalid card (CID) security code.",
|
||||
"400" => "Reversal accepted.",
|
||||
"992" => "DECLINE/TIMEOUT.",
|
||||
"107" => "Please Call Issuer.",
|
||||
"025" => "Transaction Not Found.",
|
||||
"981" => "AVS Error.",
|
||||
"913" => "Invalid Card Type.",
|
||||
"996" => "Terminal ID Not Found.",
|
||||
nil => "No response returned (missing credentials?)."
|
||||
}
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, credit_card, options = {})
|
||||
commit(money, build_sale_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def authorize(money, credit_card, options = {})
|
||||
commit(money, build_authonly_request(money, credit_card, options))
|
||||
end
|
||||
|
||||
def capture(money, reference, options = {})
|
||||
commit(money, build_capture_request('CAPT', reference.split(";").first))
|
||||
end
|
||||
|
||||
def void(reference, options = {})
|
||||
transaction_id, approval, amount = reference.split(";")
|
||||
commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval))
|
||||
end
|
||||
|
||||
def credit(money, transaction_id_or_card, options = {})
|
||||
if transaction_id_or_card.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, transaction_id_or_card, options)
|
||||
else
|
||||
commit(money, build_credit_request('CREDIT', money, nil, transaction_id_or_card))
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
transaction_id = reference.split(";").first
|
||||
credit_card = options[:credit_card]
|
||||
commit(money, build_credit_request('CREDIT', money, transaction_id, credit_card))
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def build_xml_request(transaction_type, transaction_id = nil, &block)
|
||||
xml = Builder::XmlMarkup.new
|
||||
xml.tag! 'JetPay' do
|
||||
# The basic values needed for any request
|
||||
xml.tag! 'TerminalID', @options[:login]
|
||||
xml.tag! 'TransactionType', transaction_type
|
||||
xml.tag! 'TransactionID', transaction_id.nil? ? generate_unique_id.slice(0, 18) : transaction_id
|
||||
|
||||
if block_given?
|
||||
yield xml
|
||||
else
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_sale_request(money, credit_card, options)
|
||||
build_xml_request('SALE') do |xml|
|
||||
add_credit_card(xml, credit_card)
|
||||
add_addresses(xml, options)
|
||||
add_customer_data(xml, options)
|
||||
add_invoice_data(xml, options)
|
||||
xml.tag! 'TotalAmount', amount(money)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def build_authonly_request(money, credit_card, options)
|
||||
build_xml_request('AUTHONLY') do |xml|
|
||||
add_credit_card(xml, credit_card)
|
||||
add_addresses(xml, options)
|
||||
add_customer_data(xml, options)
|
||||
add_invoice_data(xml, options)
|
||||
xml.tag! 'TotalAmount', amount(money)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def build_capture_request(transaction_type, transaction_id)
|
||||
build_xml_request(transaction_type, transaction_id)
|
||||
end
|
||||
|
||||
def build_void_request(money, transaction_id, approval)
|
||||
build_xml_request('VOID', transaction_id) do |xml|
|
||||
xml.tag! 'Approval', approval
|
||||
xml.tag! 'TotalAmount', amount(money)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
# `transaction_id` may be nil for unlinked credit transactions.
|
||||
def build_credit_request(transaction_type, money, transaction_id, card)
|
||||
build_xml_request(transaction_type, transaction_id) do |xml|
|
||||
add_credit_card(xml, card) if card
|
||||
xml.tag! 'TotalAmount', amount(money)
|
||||
|
||||
xml.target!
|
||||
end
|
||||
end
|
||||
|
||||
def commit(money, request)
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, request))
|
||||
|
||||
success = success?(response)
|
||||
Response.new(success,
|
||||
success ? 'APPROVED' : message_from(response),
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => authorization_from(response, money),
|
||||
:avs_result => { :code => response[:avs] },
|
||||
:cvv_result => response[:cvv2]
|
||||
)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
return {} if body.blank?
|
||||
|
||||
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
|
||||
|
||||
def format_exp(value)
|
||||
format(value, :two_digits)
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
response[:action_code] == "000"
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
ACTION_CODE_MESSAGES[response[:action_code]]
|
||||
end
|
||||
|
||||
def authorization_from(response, money)
|
||||
original_amount = amount(money) if money
|
||||
[ response[:transaction_id], response[:approval], original_amount ].join(";")
|
||||
end
|
||||
|
||||
def add_credit_card(xml, credit_card)
|
||||
xml.tag! 'CardNum', credit_card.number
|
||||
xml.tag! 'CardExpMonth', format_exp(credit_card.month)
|
||||
xml.tag! 'CardExpYear', format_exp(credit_card.year)
|
||||
|
||||
if credit_card.first_name || credit_card.last_name
|
||||
xml.tag! 'CardName', [credit_card.first_name,credit_card.last_name].compact.join(' ')
|
||||
end
|
||||
|
||||
unless credit_card.verification_value.nil? || (credit_card.verification_value.length == 0)
|
||||
xml.tag! 'CVV2', credit_card.verification_value
|
||||
end
|
||||
end
|
||||
|
||||
def add_addresses(xml, options)
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
xml.tag! 'BillingAddress', [billing_address[:address1], billing_address[:address2]].compact.join(" ")
|
||||
xml.tag! 'BillingCity', billing_address[:city]
|
||||
xml.tag! 'BillingStateProv', billing_address[:state]
|
||||
xml.tag! 'BillingPostalCode', billing_address[:zip]
|
||||
xml.tag! 'BillingCountry', lookup_country_code(billing_address[:country])
|
||||
xml.tag! 'BillingPhone', billing_address[:phone]
|
||||
end
|
||||
|
||||
if shipping_address = options[:shipping_address]
|
||||
xml.tag! 'ShippingInfo' do
|
||||
xml.tag! 'ShippingName', shipping_address[:name]
|
||||
|
||||
xml.tag! 'ShippingAddr' do
|
||||
xml.tag! 'Address', [shipping_address[:address1], shipping_address[:address2]].compact.join(" ")
|
||||
xml.tag! 'City', shipping_address[:city]
|
||||
xml.tag! 'StateProv', shipping_address[:state]
|
||||
xml.tag! 'PostalCode', shipping_address[:zip]
|
||||
xml.tag! 'Country', lookup_country_code(shipping_address[:country])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_customer_data(xml, options)
|
||||
xml.tag! 'Email', options[:email] if options[:email]
|
||||
xml.tag! 'UserIPAddress', options[:ip] if options[:ip]
|
||||
end
|
||||
|
||||
def add_invoice_data(xml, options)
|
||||
xml.tag! 'OrderNumber', options[:order_id] if options[:order_id]
|
||||
xml.tag! 'TaxAmount', amount(options[:tax]) if options[:tax]
|
||||
end
|
||||
|
||||
def lookup_country_code(code)
|
||||
country = Country.find(code) rescue nil
|
||||
country && country.code(:alpha3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
447
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/linkpoint.rb
vendored
Normal file
447
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/linkpoint.rb
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
require 'rexml/document'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
|
||||
# Initialization Options
|
||||
# :login Your store number
|
||||
# :pem The text of your linkpoint PEM file. Note
|
||||
# this is not the path to file, but its
|
||||
# contents. If you are only using one PEM
|
||||
# file on your site you can declare it
|
||||
# globally and then you won't need to
|
||||
# include this option
|
||||
#
|
||||
#
|
||||
# A valid store number is required. Unfortunately, with LinkPoint
|
||||
# YOU CAN'T JUST USE ANY OLD STORE NUMBER. Also, you can't just
|
||||
# generate your own PEM file. You'll need to use a special PEM file
|
||||
# provided by LinkPoint.
|
||||
#
|
||||
# Go to http://www.linkpoint.com/support/sup_teststore.asp to set up
|
||||
# a test account and obtain your PEM file.
|
||||
#
|
||||
# Declaring PEM file Globally
|
||||
# ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' )
|
||||
#
|
||||
#
|
||||
# Valid Order Options
|
||||
# :result =>
|
||||
# LIVE Production mode
|
||||
# GOOD Approved response in test mode
|
||||
# DECLINE Declined response in test mode
|
||||
# DUPLICATE Duplicate response in test mode
|
||||
#
|
||||
# :ponumber Order number
|
||||
#
|
||||
# :transactionorigin => Source of the transaction
|
||||
# ECI Email or Internet
|
||||
# MAIL Mail order
|
||||
# MOTO Mail order/Telephone
|
||||
# TELEPHONE Telephone
|
||||
# RETAIL Face-to-face
|
||||
#
|
||||
# :ordertype =>
|
||||
# SALE Real live sale
|
||||
# PREAUTH Authorize only
|
||||
# POSTAUTH Forced Ticket or Ticket Only transaction
|
||||
# VOID
|
||||
# CREDIT
|
||||
# CALCSHIPPING For shipping charges calculations
|
||||
# CALCTAX For sales tax calculations
|
||||
#
|
||||
# Recurring Options
|
||||
# :action =>
|
||||
# SUBMIT
|
||||
# MODIFY
|
||||
# CANCEL
|
||||
#
|
||||
# :installments Identifies how many recurring payments to charge the customer
|
||||
# :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or "immediate"
|
||||
# :periodicity =>
|
||||
# MONTHLY
|
||||
# BIMONTHLY
|
||||
# WEEKLY
|
||||
# BIWEEKLY
|
||||
# YEARLY
|
||||
# DAILY
|
||||
# :threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant.
|
||||
# :comments Uh... comments
|
||||
#
|
||||
#
|
||||
# For reference:
|
||||
#
|
||||
# https://www.linkpointcentral.com/lpc/docs/Help/APIHelp/lpintguide.htm
|
||||
#
|
||||
# Entities = {
|
||||
# :payment => [:subtotal, :tax, :vattax, :shipping, :chargetotal],
|
||||
# :billing => [:name, :address1, :address2, :city, :state, :zip, :country, :email, :phone, :fax, :addrnum],
|
||||
# :shipping => [:name, :address1, :address2, :city, :state, :zip, :country, :weight, :items, :carrier, :total],
|
||||
# :creditcard => [:cardnumber, :cardexpmonth, :cardexpyear, :cvmvalue, :track],
|
||||
# :telecheck => [:routing, :account, :checknumber, :bankname, :bankstate, :dl, :dlstate, :void, :accounttype, :ssn],
|
||||
# :transactiondetails => [:transactionorigin, :oid, :ponumber, :taxexempt, :terminaltype, :ip, :reference_number, :recurring, :tdate],
|
||||
# :periodic => [:action, :installments, :threshold, :startdate, :periodicity, :comments],
|
||||
# :notes => [:comments, :referred]
|
||||
# :items => [:item => [:price, :quantity, :description, :id, :options => [:option => [:name, :value]]]]
|
||||
# }
|
||||
#
|
||||
#
|
||||
# LinkPoint's Items entity is an optional entity that can be attached to orders.
|
||||
# It is entered as :line_items to be consistent with the CyberSource implementation
|
||||
#
|
||||
# The line_item hash goes in the options hash and should look like
|
||||
#
|
||||
# :line_items => [
|
||||
# {
|
||||
# :id => '123456',
|
||||
# :description => 'Logo T-Shirt',
|
||||
# :price => '12.00',
|
||||
# :quantity => '1',
|
||||
# :options => [
|
||||
# {
|
||||
# :name => 'Color',
|
||||
# :value => 'Red'
|
||||
# },
|
||||
# {
|
||||
# :name => 'Size',
|
||||
# :value => 'XL'
|
||||
# }
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# :id => '111',
|
||||
# :description => 'keychain',
|
||||
# :price => '3.00',
|
||||
# :quantity => '1'
|
||||
# }
|
||||
# ]
|
||||
# This functionality is only supported by this particular gateway may
|
||||
# be changed at any time
|
||||
#
|
||||
class LinkpointGateway < Gateway
|
||||
# Your global PEM file. This will be assigned to you by linkpoint
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' )
|
||||
#
|
||||
cattr_accessor :pem_file
|
||||
|
||||
self.test_url = 'https://staging.linkpt.net:1129/'
|
||||
self.live_url = 'https://secure.linkpt.net:1129/'
|
||||
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
|
||||
self.homepage_url = 'http://www.linkpoint.com/'
|
||||
self.display_name = 'LinkPoint'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login)
|
||||
|
||||
@options = {
|
||||
:result => 'LIVE',
|
||||
:pem => LinkpointGateway.pem_file
|
||||
}.update(options)
|
||||
|
||||
raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank?
|
||||
end
|
||||
|
||||
# Send a purchase request with periodic options
|
||||
# Recurring Options
|
||||
# :action =>
|
||||
# SUBMIT
|
||||
# MODIFY
|
||||
# CANCEL
|
||||
#
|
||||
# :installments Identifies how many recurring payments to charge the customer
|
||||
# :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or "immediate"
|
||||
# :periodicity =>
|
||||
# :monthly
|
||||
# :bimonthly
|
||||
# :weekly
|
||||
# :biweekly
|
||||
# :yearly
|
||||
# :daily
|
||||
# :threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant.
|
||||
# :comments Uh... comments
|
||||
#
|
||||
def recurring(money, creditcard, options={})
|
||||
requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily], :installments, :order_id )
|
||||
|
||||
options.update(
|
||||
:ordertype => "SALE",
|
||||
:action => options[:action] || "SUBMIT",
|
||||
:installments => options[:installments] || 12,
|
||||
:startdate => options[:startdate] || "immediate",
|
||||
:periodicity => options[:periodicity].to_s || "monthly",
|
||||
:comments => options[:comments] || nil,
|
||||
:threshold => options[:threshold] || 3
|
||||
)
|
||||
commit(money, creditcard, options)
|
||||
end
|
||||
|
||||
# Buy the thing
|
||||
def purchase(money, creditcard, options={})
|
||||
requires!(options, :order_id)
|
||||
options.update(
|
||||
:ordertype => "SALE"
|
||||
)
|
||||
commit(money, creditcard, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Authorize the transaction
|
||||
#
|
||||
# Reserves the funds on the customer's credit card, but does not charge the card.
|
||||
#
|
||||
def authorize(money, creditcard, options = {})
|
||||
requires!(options, :order_id)
|
||||
options.update(
|
||||
:ordertype => "PREAUTH"
|
||||
)
|
||||
commit(money, creditcard, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Post an authorization.
|
||||
#
|
||||
# Captures the funds from an authorized transaction.
|
||||
# Order_id must be a valid order id from a prior authorized transaction.
|
||||
#
|
||||
def capture(money, authorization, options = {})
|
||||
options.update(
|
||||
:order_id => authorization,
|
||||
:ordertype => "POSTAUTH"
|
||||
)
|
||||
commit(money, nil, options)
|
||||
end
|
||||
|
||||
# Void a previous transaction
|
||||
def void(identification, options = {})
|
||||
options.update(
|
||||
:order_id => identification,
|
||||
:ordertype => "VOID"
|
||||
)
|
||||
commit(nil, nil, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Refund an order
|
||||
#
|
||||
# identification must be a valid order id previously submitted by SALE
|
||||
#
|
||||
def refund(money, identification, options = {})
|
||||
options.update(
|
||||
:ordertype => "CREDIT",
|
||||
:order_id => identification
|
||||
)
|
||||
commit(money, nil, options)
|
||||
end
|
||||
|
||||
def credit(money, identification, options = {})
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, identification, options)
|
||||
end
|
||||
|
||||
private
|
||||
# Commit the transaction by posting the XML file to the LinkPoint server
|
||||
def commit(money, creditcard, options = {})
|
||||
response = parse(ssl_post(test? ? self.test_url : self.live_url, post_data(money, creditcard, options)))
|
||||
|
||||
Response.new(successful?(response), response[:message], response,
|
||||
:test => test?,
|
||||
:authorization => response[:ordernum],
|
||||
:avs_result => { :code => response[:avs].to_s[2,1] },
|
||||
:cvv_result => response[:avs].to_s[3,1]
|
||||
)
|
||||
end
|
||||
|
||||
def successful?(response)
|
||||
response[:approved] == "APPROVED"
|
||||
end
|
||||
|
||||
# Build the XML file
|
||||
def post_data(money, creditcard, options)
|
||||
params = parameters(money, creditcard, options)
|
||||
|
||||
xml = REXML::Document.new
|
||||
order = xml.add_element("order")
|
||||
|
||||
# Merchant Info
|
||||
merchantinfo = order.add_element("merchantinfo")
|
||||
merchantinfo.add_element("configfile").text = @options[:login]
|
||||
|
||||
# Loop over the params hash to construct the XML string
|
||||
for key, value in params
|
||||
elem = order.add_element(key.to_s)
|
||||
if key == :items
|
||||
build_items(elem, value)
|
||||
else
|
||||
for k, v in params[key]
|
||||
elem.add_element(k.to_s).text = params[key][k].to_s if params[key][k]
|
||||
end
|
||||
end
|
||||
# Linkpoint doesn't understand empty elements:
|
||||
order.delete(elem) if elem.size == 0
|
||||
end
|
||||
return xml.to_s
|
||||
end
|
||||
|
||||
# adds LinkPoint's Items entity to the XML. Called from post_data
|
||||
def build_items(element, items)
|
||||
for item in items
|
||||
item_element = element.add_element("item")
|
||||
for key, value in item
|
||||
if key == :options
|
||||
options_element = item_element.add_element("options")
|
||||
for option in value
|
||||
opt_element = options_element.add_element("option")
|
||||
opt_element.add_element("name").text = option[:name] unless option[:name].blank?
|
||||
opt_element.add_element("value").text = option[:value] unless option[:value].blank?
|
||||
end
|
||||
else
|
||||
item_element.add_element(key.to_s).text = item[key].to_s unless item[key].blank?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Set up the parameters hash just once so we don't have to do it
|
||||
# for every action.
|
||||
def parameters(money, creditcard, options = {})
|
||||
|
||||
params = {
|
||||
:payment => {
|
||||
:subtotal => amount(options[:subtotal]),
|
||||
:tax => amount(options[:tax]),
|
||||
:vattax => amount(options[:vattax]),
|
||||
:shipping => amount(options[:shipping]),
|
||||
:chargetotal => amount(money)
|
||||
},
|
||||
:transactiondetails => {
|
||||
:transactionorigin => options[:transactionorigin] || "ECI",
|
||||
:oid => options[:order_id],
|
||||
:ponumber => options[:ponumber],
|
||||
:taxexempt => options[:taxexempt],
|
||||
:terminaltype => options[:terminaltype],
|
||||
:ip => options[:ip],
|
||||
:reference_number => options[:reference_number],
|
||||
:recurring => options[:recurring] || "NO", #DO NOT USE if you are using the periodic billing option.
|
||||
:tdate => options[:tdate]
|
||||
},
|
||||
:orderoptions => {
|
||||
:ordertype => options[:ordertype],
|
||||
:result => @options[:result]
|
||||
},
|
||||
:periodic => {
|
||||
:action => options[:action],
|
||||
:installments => options[:installments],
|
||||
:threshold => options[:threshold],
|
||||
:startdate => options[:startdate],
|
||||
:periodicity => options[:periodicity],
|
||||
:comments => options[:comments]
|
||||
},
|
||||
:telecheck => {
|
||||
:routing => options[:telecheck_routing],
|
||||
:account => options[:telecheck_account],
|
||||
:checknumber => options[:telecheck_checknumber],
|
||||
:bankname => options[:telecheck_bankname],
|
||||
:dl => options[:telecheck_dl],
|
||||
:dlstate => options[:telecheck_dlstate],
|
||||
:void => options[:telecheck_void],
|
||||
:accounttype => options[:telecheck_accounttype],
|
||||
:ssn => options[:telecheck_ssn],
|
||||
}
|
||||
}
|
||||
|
||||
if creditcard
|
||||
params[:creditcard] = {
|
||||
:cardnumber => creditcard.number,
|
||||
:cardexpmonth => creditcard.month,
|
||||
:cardexpyear => format_creditcard_expiry_year(creditcard.year),
|
||||
:track => nil
|
||||
}
|
||||
|
||||
if creditcard.verification_value?
|
||||
params[:creditcard][:cvmvalue] = creditcard.verification_value
|
||||
params[:creditcard][:cvmindicator] = 'provided'
|
||||
else
|
||||
params[:creditcard][:cvmindicator] = 'not_provided'
|
||||
end
|
||||
end
|
||||
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
|
||||
params[:billing] = {}
|
||||
params[:billing][:name] = billing_address[:name] || (creditcard ? creditcard.name : nil)
|
||||
params[:billing][:address1] = billing_address[:address1] unless billing_address[:address1].blank?
|
||||
params[:billing][:address2] = billing_address[:address2] unless billing_address[:address2].blank?
|
||||
params[:billing][:city] = billing_address[:city] unless billing_address[:city].blank?
|
||||
params[:billing][:state] = billing_address[:state] unless billing_address[:state].blank?
|
||||
params[:billing][:zip] = billing_address[:zip] unless billing_address[:zip].blank?
|
||||
params[:billing][:country] = billing_address[:country] unless billing_address[:country].blank?
|
||||
params[:billing][:company] = billing_address[:company] unless billing_address[:company].blank?
|
||||
params[:billing][:phone] = billing_address[:phone] unless billing_address[:phone].blank?
|
||||
params[:billing][:email] = options[:email] unless options[:email].blank?
|
||||
end
|
||||
|
||||
if shipping_address = options[:shipping_address]
|
||||
|
||||
params[:shipping] = {}
|
||||
params[:shipping][:name] = shipping_address[:name] || (creditcard ? creditcard.name : nil)
|
||||
params[:shipping][:address1] = shipping_address[:address1] unless shipping_address[:address1].blank?
|
||||
params[:shipping][:address2] = shipping_address[:address2] unless shipping_address[:address2].blank?
|
||||
params[:shipping][:city] = shipping_address[:city] unless shipping_address[:city].blank?
|
||||
params[:shipping][:state] = shipping_address[:state] unless shipping_address[:state].blank?
|
||||
params[:shipping][:zip] = shipping_address[:zip] unless shipping_address[:zip].blank?
|
||||
params[:shipping][:country] = shipping_address[:country] unless shipping_address[:country].blank?
|
||||
end
|
||||
|
||||
params[:items] = options[:line_items] if options[:line_items]
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
def parse(xml)
|
||||
|
||||
# For reference, a typical response...
|
||||
# <r_csp></r_csp>
|
||||
# <r_time></r_time>
|
||||
# <r_ref></r_ref>
|
||||
# <r_error></r_error>
|
||||
# <r_ordernum></r_ordernum>
|
||||
# <r_message>This is a test transaction and will not show up in the Reports</r_message>
|
||||
# <r_code></r_code>
|
||||
# <r_tdate>Thu Feb 2 15:40:21 2006</r_tdate>
|
||||
# <r_score></r_score>
|
||||
# <r_authresponse></r_authresponse>
|
||||
# <r_approved>APPROVED</r_approved>
|
||||
# <r_avs></r_avs>
|
||||
|
||||
response = {:message => "Global Error Receipt", :complete => false}
|
||||
|
||||
xml = REXML::Document.new("<response>#{xml}</response>")
|
||||
xml.root.elements.each do |node|
|
||||
response[node.name.downcase.sub(/^r_/, '').to_sym] = normalize(node.text)
|
||||
end unless xml.root.nil?
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# Make a ruby type out of the response string
|
||||
def normalize(field)
|
||||
case field
|
||||
when "true" then true
|
||||
when "false" then false
|
||||
when "" then nil
|
||||
when "null" then nil
|
||||
else field
|
||||
end
|
||||
end
|
||||
|
||||
def format_creditcard_expiry_year(year)
|
||||
sprintf("%.4i", year)[-2..-1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
540
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/litle.rb
vendored
Executable file
540
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/litle.rb
vendored
Executable file
@@ -0,0 +1,540 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class LitleGateway < Gateway
|
||||
# Specific to Litle options:
|
||||
# * <tt>:merchant_id</tt> - Merchant Id assigned by Litle
|
||||
# * <tt>:user</tt> - Username assigned by Litle
|
||||
# * <tt>:password</tt> - Password assigned by Litle
|
||||
# * <tt>:version</tt> - The version of the api you are using (eg, '8.10')
|
||||
# * <tt>:proxy_addr</tt> - Proxy address - nil if not needed
|
||||
# * <tt>:proxy_port</tt> - Proxy port - nil if not needed
|
||||
# * <tt>:url</tt> - URL assigned by Litle (for testing, use the sandbox)
|
||||
#
|
||||
# Standard Active Merchant options
|
||||
# * <tt>:order_id</tt> - The order number
|
||||
# * <tt>:ip</tt> - The IP address of the customer making the purchase
|
||||
# * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
|
||||
# * <tt>:invoice</tt> - The invoice number
|
||||
# * <tt>:merchant</tt> - The name or description of the merchant offering the product
|
||||
# * <tt>:description</tt> - A description of the transaction
|
||||
# * <tt>:email</tt> - The email address of the customer
|
||||
# * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
|
||||
# * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
|
||||
# * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
|
||||
#
|
||||
# The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
|
||||
#
|
||||
# * <tt>:name</tt> - The full name of the customer.
|
||||
# * <tt>:company</tt> - The company name of the customer.
|
||||
# * <tt>:address1</tt> - The primary street address of the customer.
|
||||
# * <tt>:address2</tt> - Additional line of address information.
|
||||
# * <tt>:city</tt> - The city of the customer.
|
||||
# * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
|
||||
# * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
|
||||
# * <tt>:zip</tt> - The zip or postal code of the customer.
|
||||
# * <tt>:phone</tt> - The phone number of the customer.
|
||||
|
||||
self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
|
||||
self.live_url = 'https://payments.litle.com/vap/communicator/online'
|
||||
|
||||
LITLE_SCHEMA_VERSION = '8.13'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.litle.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Litle & Co.'
|
||||
|
||||
self.default_currency = 'USD'
|
||||
|
||||
def initialize(options = {})
|
||||
begin
|
||||
require 'LitleOnline'
|
||||
rescue LoadError
|
||||
raise "Could not load the LitleOnline gem (>= 08.13.2). Use `gem install LitleOnline` to install it."
|
||||
end
|
||||
|
||||
@litle = LitleOnline::LitleOnlineRequest.new
|
||||
|
||||
options[:version] ||= LITLE_SCHEMA_VERSION
|
||||
options[:merchant] ||= 'Default Report Group'
|
||||
options[:user] ||= options[:login]
|
||||
|
||||
requires!(options, :merchant_id, :user, :password, :merchant, :version)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard_or_token, options = {})
|
||||
to_pass = build_authorize_request(money, creditcard_or_token, options)
|
||||
build_response(:authorization, @litle.authorization(to_pass))
|
||||
end
|
||||
|
||||
def purchase(money, creditcard_or_token, options = {})
|
||||
to_pass = build_purchase_request(money, creditcard_or_token, options)
|
||||
build_response(:sale, @litle.sale(to_pass))
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
transaction_id, kind = split_authorization(authorization)
|
||||
to_pass = create_capture_hash(money, transaction_id, options)
|
||||
build_response(:capture, @litle.capture(to_pass))
|
||||
end
|
||||
|
||||
# Note: Litle requires that authorization requests be voided via auth_reversal
|
||||
# and other requests via void. To maintain the same interface as the other
|
||||
# gateways the transaction_id and the kind of transaction are concatenated
|
||||
# together with a ; separator (e.g. 1234;authorization)
|
||||
#
|
||||
# A partial auth_reversal can be accomplished by passing :amount as an option
|
||||
def void(identification, options = {})
|
||||
transaction_id, kind = split_authorization(identification)
|
||||
if(kind == 'authorization')
|
||||
to_pass = create_auth_reversal_hash(transaction_id, options[:amount], options)
|
||||
build_response(:authReversal, @litle.auth_reversal(to_pass))
|
||||
else
|
||||
to_pass = create_void_hash(transaction_id, options)
|
||||
build_response(:void, @litle.void(to_pass))
|
||||
end
|
||||
end
|
||||
|
||||
def credit(money, identification_or_token, options = {})
|
||||
to_pass = build_credit_request(money, identification_or_token, options)
|
||||
build_response(:credit, @litle.credit(to_pass))
|
||||
end
|
||||
|
||||
def store(creditcard, options = {})
|
||||
to_pass = create_token_hash(creditcard, options)
|
||||
build_response(:registerToken, @litle.register_token_request(to_pass), %w(000 801 802))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CARD_TYPE = {
|
||||
'visa' => 'VI',
|
||||
'master' => 'MC',
|
||||
'american_express' => 'AX',
|
||||
'discover' => 'DI',
|
||||
'jcb' => 'DI',
|
||||
'diners_club' => 'DI'
|
||||
}
|
||||
|
||||
AVS_RESPONSE_CODE = {
|
||||
'00' => 'Y',
|
||||
'01' => 'X',
|
||||
'02' => 'D',
|
||||
'10' => 'Z',
|
||||
'11' => 'W',
|
||||
'12' => 'A',
|
||||
'13' => 'A',
|
||||
'14' => 'P',
|
||||
'20' => 'N',
|
||||
'30' => 'S',
|
||||
'31' => 'R',
|
||||
'32' => 'U',
|
||||
'33' => 'R',
|
||||
'34' => 'I',
|
||||
'40' => 'E'
|
||||
}
|
||||
|
||||
def url
|
||||
return @options[:url] if @options[:url].present?
|
||||
|
||||
test? ? self.test_url : self.live_url
|
||||
end
|
||||
|
||||
def build_response(kind, litle_response, valid_responses=%w(000))
|
||||
response = Hash.from_xml(litle_response.raw_xml.to_s)['litleOnlineResponse']
|
||||
|
||||
if response['response'] == "0"
|
||||
detail = response["#{kind}Response"]
|
||||
fraud = fraud_result(detail)
|
||||
Response.new(
|
||||
valid_responses.include?(detail['response']),
|
||||
detail['message'],
|
||||
{ :litleOnlineResponse => response },
|
||||
:authorization => authorization_from(detail, kind),
|
||||
:avs_result => { :code => fraud['avs'] },
|
||||
:cvv_result => fraud['cvv'],
|
||||
:test => test?
|
||||
)
|
||||
else
|
||||
Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?)
|
||||
end
|
||||
end
|
||||
|
||||
# Generates an authorization string of the appropriate id and the kind of transaction
|
||||
# See #void for how the kind is used
|
||||
def authorization_from(litle_response, kind)
|
||||
case kind
|
||||
when :registerToken
|
||||
authorization = litle_response['litleToken']
|
||||
else
|
||||
authorization = [litle_response['litleTxnId'], kind.to_s].join(";")
|
||||
end
|
||||
end
|
||||
|
||||
def split_authorization(authorization)
|
||||
transaction_id, kind = authorization.to_s.split(';')
|
||||
[transaction_id, kind]
|
||||
end
|
||||
|
||||
def build_authorize_request(money, creditcard_or_token, options)
|
||||
payment_method = build_payment_method(creditcard_or_token, options)
|
||||
|
||||
hash = create_hash(money, options)
|
||||
|
||||
add_creditcard_or_cardtoken_hash(hash, payment_method)
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def build_purchase_request(money, creditcard_or_token, options)
|
||||
payment_method = build_payment_method(creditcard_or_token, options)
|
||||
|
||||
hash = create_hash(money, options)
|
||||
|
||||
add_creditcard_or_cardtoken_hash(hash, payment_method)
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def build_credit_request(money, identification_or_token, options)
|
||||
payment_method = build_payment_method(identification_or_token, options)
|
||||
|
||||
hash = create_hash(money, options)
|
||||
|
||||
add_identification_or_cardtoken_hash(hash, payment_method)
|
||||
|
||||
unless payment_method.is_a?(LitleCardToken)
|
||||
hash['orderSource'] = nil
|
||||
hash['orderId'] = nil
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def build_payment_method(payment_method, options)
|
||||
result = payment_method
|
||||
|
||||
# Build instance of the LitleCardToken class for internal use if this is a token request.
|
||||
if payment_method.is_a?(String) && options.has_key?(:token)
|
||||
result = LitleCardToken.new(:token => payment_method)
|
||||
result.month = options[:token][:month]
|
||||
result.year = options[:token][:year]
|
||||
result.verification_value = options[:token][:verification_value]
|
||||
result.brand = options[:token][:brand]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def add_creditcard_or_cardtoken_hash(hash, creditcard_or_cardtoken)
|
||||
if creditcard_or_cardtoken.is_a?(LitleCardToken)
|
||||
add_cardtoken_hash(hash, creditcard_or_cardtoken)
|
||||
else
|
||||
add_creditcard_hash(hash, creditcard_or_cardtoken)
|
||||
end
|
||||
end
|
||||
|
||||
def add_identification_or_cardtoken_hash(hash, identification_or_cardtoken)
|
||||
if identification_or_cardtoken.is_a?(LitleCardToken)
|
||||
add_cardtoken_hash(hash, identification_or_cardtoken)
|
||||
else
|
||||
transaction_id, kind = split_authorization(identification_or_cardtoken)
|
||||
hash['litleTxnId'] = transaction_id
|
||||
end
|
||||
end
|
||||
|
||||
def add_cardtoken_hash(hash, cardtoken)
|
||||
token_info = {}
|
||||
token_info['litleToken'] = cardtoken.token
|
||||
token_info['expDate'] = cardtoken.exp_date if cardtoken.exp_date?
|
||||
token_info['cardValidationNum'] = cardtoken.verification_value unless cardtoken.verification_value.blank?
|
||||
token_info['type'] = cardtoken.type unless cardtoken.type.blank?
|
||||
|
||||
hash['token'] = token_info
|
||||
hash
|
||||
end
|
||||
|
||||
def add_creditcard_hash(hash, creditcard)
|
||||
cc_type = CARD_TYPE[creditcard.brand]
|
||||
exp_date_yr = creditcard.year.to_s[2..3]
|
||||
exp_date_mo = '%02d' % creditcard.month.to_i
|
||||
exp_date = exp_date_mo + exp_date_yr
|
||||
|
||||
card_info = {
|
||||
'type' => cc_type,
|
||||
'number' => creditcard.number,
|
||||
'expDate' => exp_date,
|
||||
'cardValidationNum' => creditcard.verification_value
|
||||
}
|
||||
|
||||
hash['card'] = card_info
|
||||
hash
|
||||
end
|
||||
|
||||
def create_capture_hash(money, authorization, options)
|
||||
hash = create_hash(money, options)
|
||||
hash['litleTxnId'] = authorization
|
||||
hash
|
||||
end
|
||||
|
||||
def create_token_hash(creditcard, options)
|
||||
hash = create_hash(0, options)
|
||||
hash['accountNumber'] = creditcard.number
|
||||
hash
|
||||
end
|
||||
|
||||
def create_void_hash(identification, options)
|
||||
hash = create_hash(nil, options)
|
||||
hash['litleTxnId'] = identification
|
||||
hash
|
||||
end
|
||||
|
||||
def create_auth_reversal_hash(identification, money, options)
|
||||
hash = create_hash(money, options)
|
||||
hash['litleTxnId'] = identification
|
||||
hash
|
||||
end
|
||||
|
||||
def create_hash(money, options)
|
||||
fraud_check_type = {}
|
||||
if options[:ip]
|
||||
fraud_check_type['customerIpAddress'] = options[:ip]
|
||||
end
|
||||
|
||||
enhanced_data = {}
|
||||
if options[:invoice]
|
||||
enhanced_data['invoiceReferenceNumber'] = options[:invoice]
|
||||
end
|
||||
|
||||
if options[:description]
|
||||
enhanced_data['customerReference'] = options[:description]
|
||||
end
|
||||
|
||||
if options[:billing_address]
|
||||
bill_to_address = {
|
||||
'name' => options[:billing_address][:name],
|
||||
'companyName' => options[:billing_address][:company],
|
||||
'addressLine1' => options[:billing_address][:address1],
|
||||
'addressLine2' => options[:billing_address][:address2],
|
||||
'city' => options[:billing_address][:city],
|
||||
'state' => options[:billing_address][:state],
|
||||
'zip' => options[:billing_address][:zip],
|
||||
'country' => options[:billing_address][:country],
|
||||
'email' => options[:email],
|
||||
'phone' => options[:billing_address][:phone]
|
||||
}
|
||||
end
|
||||
if options[:shipping_address]
|
||||
ship_to_address = {
|
||||
'name' => options[:shipping_address][:name],
|
||||
'companyName' => options[:shipping_address][:company],
|
||||
'addressLine1' => options[:shipping_address][:address1],
|
||||
'addressLine2' => options[:shipping_address][:address2],
|
||||
'city' => options[:shipping_address][:city],
|
||||
'state' => options[:shipping_address][:state],
|
||||
'zip' => options[:shipping_address][:zip],
|
||||
'country' => options[:shipping_address][:country],
|
||||
'email' => options[:email],
|
||||
'phone' => options[:shipping_address][:phone]
|
||||
}
|
||||
end
|
||||
|
||||
hash = {
|
||||
'billToAddress' => bill_to_address,
|
||||
'shipToAddress' => ship_to_address,
|
||||
'orderId' => (options[:order_id] || @options[:order_id]),
|
||||
'customerId' => options[:customer],
|
||||
'reportGroup' => (options[:merchant] || @options[:merchant]),
|
||||
'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
|
||||
'orderSource' => (options[:order_source] || 'ecommerce'),
|
||||
'enhancedData' => enhanced_data,
|
||||
'fraudCheckType' => fraud_check_type,
|
||||
'user' => (options[:user] || @options[:user]),
|
||||
'password' => (options[:password] || @options[:password]),
|
||||
'version' => (options[:version] || @options[:version]),
|
||||
'url' => (options[:url] || url),
|
||||
'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
|
||||
'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
|
||||
'id' => (options[:id] || options[:order_id] || @options[:order_id])
|
||||
}
|
||||
|
||||
if (!money.nil? && money.to_s.length > 0)
|
||||
hash.merge!({ 'amount' => money })
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
def fraud_result(authorization_response)
|
||||
if result = authorization_response['fraudResult']
|
||||
if result.key?('cardValidationResult')
|
||||
cvv_to_pass = result['cardValidationResult'].blank? ? "P" : result['cardValidationResult']
|
||||
end
|
||||
|
||||
avs_to_pass = AVS_RESPONSE_CODE[result['avsResult']] unless result['avsResult'].blank?
|
||||
end
|
||||
{ 'cvv' => cvv_to_pass, 'avs' => avs_to_pass }
|
||||
end
|
||||
|
||||
# A +LitleCardToken+ object represents a tokenized credit card, and is capable of validating the various
|
||||
# data associated with these.
|
||||
#
|
||||
# == Example Usage
|
||||
# token = LitleCardToken.new(
|
||||
# :token => '1234567890123456',
|
||||
# :month => '9',
|
||||
# :year => '2010',
|
||||
# :brand => 'visa',
|
||||
# :verification_value => '123'
|
||||
# )
|
||||
#
|
||||
# token.valid? # => true
|
||||
# cc.exp_date # => 0910
|
||||
#
|
||||
class LitleCardToken
|
||||
include Validateable
|
||||
|
||||
# Returns or sets the token. (required)
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :token
|
||||
|
||||
# Returns or sets the expiry month for the card associated with token. (optional)
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_accessor :month
|
||||
|
||||
# Returns or sets the expiry year for the card associated with token. (optional)
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_accessor :year
|
||||
|
||||
# Returns or sets the card verification value. (optional)
|
||||
#
|
||||
# @return [String] the verification value
|
||||
attr_accessor :verification_value
|
||||
|
||||
# Returns or sets the credit card brand. (optional)
|
||||
#
|
||||
# Valid card types are
|
||||
#
|
||||
# * +'visa'+
|
||||
# * +'master'+
|
||||
# * +'discover'+
|
||||
# * +'american_express'+
|
||||
# * +'diners_club'+
|
||||
# * +'jcb'+
|
||||
# * +'switch'+
|
||||
# * +'solo'+
|
||||
# * +'dankort'+
|
||||
# * +'maestro'+
|
||||
# * +'forbrugsforeningen'+
|
||||
# * +'laser'+
|
||||
#
|
||||
# @return (String) the credit card brand
|
||||
attr_accessor :brand
|
||||
|
||||
# Returns the Litle credit card type identifier.
|
||||
#
|
||||
# @return (String) the credit card type identifier
|
||||
def type
|
||||
CARD_TYPE[brand] unless brand.blank?
|
||||
end
|
||||
|
||||
# Returns true if the expiration date is set.
|
||||
#
|
||||
# @return (Boolean)
|
||||
def exp_date?
|
||||
!month.to_i.zero? && !year.to_i.zero?
|
||||
end
|
||||
|
||||
# Returns the card token expiration date in MMYY format.
|
||||
#
|
||||
# @return (String) the expiration date in MMYY format
|
||||
def exp_date
|
||||
result = ''
|
||||
if exp_date?
|
||||
exp_date_yr = year.to_s[2..3]
|
||||
exp_date_mo = '%02d' % month.to_i
|
||||
|
||||
result = exp_date_mo + exp_date_yr
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Validates the card token details.
|
||||
#
|
||||
# Any validation errors are added to the {#errors} attribute.
|
||||
def validate
|
||||
validate_card_token
|
||||
validate_expiration_date
|
||||
validate_card_brand
|
||||
end
|
||||
|
||||
def check?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CARD_TYPE = {
|
||||
'visa' => 'VI',
|
||||
'master' => 'MC',
|
||||
'american_express' => 'AX',
|
||||
'discover' => 'DI',
|
||||
'jcb' => 'DI',
|
||||
'diners_club' => 'DI'
|
||||
}
|
||||
|
||||
def before_validate #:nodoc:
|
||||
self.month = month.to_i
|
||||
self.year = year.to_i
|
||||
end
|
||||
|
||||
# Litle XML Reference Guide 1.8.2
|
||||
#
|
||||
# The length of the original card number is reflected in the token, so a
|
||||
# submitted 16-digit number results in a 16-digit token. Also, all tokens
|
||||
# use only numeric characters, so you do not have to change your
|
||||
# systems to accept alpha-numeric characters.
|
||||
#
|
||||
# The credit card token numbers themselves have two parts.
|
||||
# The last four digits match the last four digits of the card number.
|
||||
# The remaining digits (length can vary based upon original card number
|
||||
# length) are a randomly generated.
|
||||
def validate_card_token #:nodoc:
|
||||
if token.to_s.length < 12 || token.to_s.match(/\A\d+\Z/).nil?
|
||||
errors.add :token, "is not a valid card token"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_expiration_date #:nodoc:
|
||||
if !month.to_i.zero? || !year.to_i.zero?
|
||||
errors.add :month, "is not a valid month" unless valid_month?(month)
|
||||
errors.add :year, "is not a valid year" unless valid_expiry_year?(year)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_card_brand #:nodoc:
|
||||
errors.add :brand, "is invalid" unless brand.blank? || CreditCard.card_companies.keys.include?(brand)
|
||||
end
|
||||
|
||||
def valid_month?(month)
|
||||
(1..12).include?(month.to_i)
|
||||
end
|
||||
|
||||
def valid_expiry_year?(year)
|
||||
year.to_s =~ /\A\d{4}\Z/ && year.to_i > 1987
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
176
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_e_solutions.rb
vendored
Normal file
176
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_e_solutions.rb
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class MerchantESolutionsGateway < Gateway
|
||||
self.test_url = 'https://cert.merchante-solutions.com/mes-api/tridentApi'
|
||||
self.live_url = 'https://api.merchante-solutions.com/mes-api/tridentApi'
|
||||
|
||||
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
||||
self.supported_countries = ['US']
|
||||
|
||||
# The card types supported by the payment gateway
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb]
|
||||
|
||||
# The homepage URL of the gateway
|
||||
self.homepage_url = 'http://www.merchante-solutions.com/'
|
||||
|
||||
# The name of the gateway
|
||||
self.display_name = 'Merchant e-Solutions'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, creditcard_or_card_id, options = {})
|
||||
post = {}
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
post[:moto_ecommerce_ind] = options[:moto_ecommerce_ind] if options.has_key?(:moto_ecommerce_ind)
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, creditcard_or_card_id, options)
|
||||
add_address(post, options)
|
||||
commit('P', money, post)
|
||||
end
|
||||
|
||||
def purchase(money, creditcard_or_card_id, options = {})
|
||||
post = {}
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
post[:moto_ecommerce_ind] = options[:moto_ecommerce_ind] if options.has_key?(:moto_ecommerce_ind)
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, creditcard_or_card_id, options)
|
||||
add_address(post, options)
|
||||
commit('D', money, post)
|
||||
end
|
||||
|
||||
def capture(money, transaction_id, options = {})
|
||||
post ={}
|
||||
post[:transaction_id] = transaction_id
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
commit('S', money, post)
|
||||
end
|
||||
|
||||
def store(creditcard, options = {})
|
||||
post = {}
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
add_creditcard(post, creditcard, options)
|
||||
commit('T', nil, post)
|
||||
end
|
||||
|
||||
def unstore(card_id)
|
||||
post = {}
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
post[:card_id] = card_id
|
||||
commit('X', nil, post)
|
||||
end
|
||||
|
||||
def refund(money, identification, options = {})
|
||||
post = {}
|
||||
post[:transaction_id] = identification
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
options.delete(:customer)
|
||||
options.delete(:billing_address)
|
||||
commit('U', money, options.merge(post))
|
||||
end
|
||||
|
||||
def credit(money, creditcard_or_card_id, options = {})
|
||||
post = {}
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
add_invoice(post, options)
|
||||
add_payment_source(post, creditcard_or_card_id, options)
|
||||
commit('C', money, post)
|
||||
end
|
||||
|
||||
def void(transaction_id, options = {})
|
||||
post = {}
|
||||
post[:transaction_id] = transaction_id
|
||||
post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
|
||||
options.delete(:customer)
|
||||
options.delete(:billing_address)
|
||||
commit('V', nil, options.merge(post))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_address(post, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
post[:cardholder_street_address] = address[:address1].to_s.gsub(/[^\w.]/, '+')
|
||||
post[:cardholder_zip] = address[:zip].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def add_invoice(post, options)
|
||||
if options.has_key? :order_id
|
||||
post[:invoice_number] = options[:order_id].to_s.gsub(/[^\w.]/, '')
|
||||
end
|
||||
end
|
||||
|
||||
def add_payment_source(post, creditcard_or_card_id, options)
|
||||
if creditcard_or_card_id.is_a?(String)
|
||||
# using stored card
|
||||
post[:card_id] = creditcard_or_card_id
|
||||
post[:card_exp_date] = options[:expiration_date] if options[:expiration_date]
|
||||
else
|
||||
# card info is provided
|
||||
add_creditcard(post, creditcard_or_card_id, options)
|
||||
end
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard, options)
|
||||
post[:card_number] = creditcard.number
|
||||
post[:cvv2] = creditcard.verification_value if creditcard.verification_value?
|
||||
post[:card_exp_date] = expdate(creditcard)
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
results = {}
|
||||
body.split(/&/).each do |pair|
|
||||
key,val = pair.split(/=/)
|
||||
results[key] = val
|
||||
end
|
||||
results
|
||||
end
|
||||
|
||||
def commit(action, money, parameters)
|
||||
url = test? ? self.test_url : self.live_url
|
||||
parameters[:transaction_amount] = amount(money) if money unless action == 'V'
|
||||
|
||||
|
||||
response = begin
|
||||
parse( ssl_post(url, post_data(action,parameters)) )
|
||||
rescue ActiveMerchant::ResponseError => e
|
||||
{ "error_code" => "404", "auth_response_text" => e.to_s }
|
||||
end
|
||||
|
||||
Response.new(response["error_code"] == "000", message_from(response), response,
|
||||
:authorization => response["transaction_id"],
|
||||
:test => test?,
|
||||
:cvv_result => response["cvv2_result"],
|
||||
:avs_result => { :code => response["avs_result"] }
|
||||
)
|
||||
end
|
||||
|
||||
def expdate(creditcard)
|
||||
year = sprintf("%.4i", creditcard.year)
|
||||
month = sprintf("%.2i", creditcard.month)
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
if response["error_code"] == "000"
|
||||
"This transaction has been approved"
|
||||
else
|
||||
response["auth_response_text"]
|
||||
end
|
||||
end
|
||||
|
||||
def post_data(action, parameters = {})
|
||||
post = {}
|
||||
post[:profile_id] = @options[:login]
|
||||
post[:profile_key] = @options[:password]
|
||||
post[:transaction_type] = action if action
|
||||
|
||||
request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
|
||||
request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
323
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_ware.rb
vendored
Normal file
323
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_ware.rb
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class MerchantWareGateway < Gateway
|
||||
class_attribute :v4_live_url
|
||||
|
||||
self.live_url = self.test_url = 'https://ps1.merchantware.net/MerchantWARE/ws/RetailTransaction/TXRetail.asmx'
|
||||
self.v4_live_url = 'https://ps1.merchantware.net/Merchantware/ws/RetailTransaction/v4/Credit.asmx'
|
||||
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
||||
self.homepage_url = 'http://merchantwarehouse.com/merchantware'
|
||||
self.display_name = 'MerchantWARE'
|
||||
|
||||
ENV_NAMESPACES = { "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
||||
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
|
||||
"xmlns:env" => "http://schemas.xmlsoap.org/soap/envelope/"
|
||||
}
|
||||
ENV_NAMESPACES_V4 = { "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/"
|
||||
}
|
||||
|
||||
TX_NAMESPACE = "http://merchantwarehouse.com/MerchantWARE/Client/TransactionRetail"
|
||||
TX_NAMESPACE_V4 = "http://schemas.merchantwarehouse.com/merchantware/40/Credit/"
|
||||
|
||||
ACTIONS = {
|
||||
:purchase => "IssueKeyedSale",
|
||||
:authorize => "IssueKeyedPreAuth",
|
||||
:capture => "IssuePostAuth",
|
||||
:void => "VoidPreAuthorization",
|
||||
:credit => "IssueKeyedRefund",
|
||||
:reference_credit => "IssueRefundByReference"
|
||||
}
|
||||
|
||||
# Creates a new MerchantWareGateway
|
||||
#
|
||||
# The gateway requires that a valid login, password, and name be passed
|
||||
# in the +options+ hash.
|
||||
#
|
||||
# ==== Options
|
||||
#
|
||||
# * <tt>:login</tt> - The MerchantWARE SiteID.
|
||||
# * <tt>:password</tt> - The MerchantWARE Key.
|
||||
# * <tt>:name</tt> - The MerchantWARE Name.
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password, :name)
|
||||
super
|
||||
end
|
||||
|
||||
# Authorize a credit card for a given amount.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be authorized as an Integer value in cents.
|
||||
# * <tt>credit_card</tt> - The CreditCard details for the transaction.
|
||||
# * <tt>options</tt>
|
||||
# * <tt>:order_id</tt> - A unique reference for this order (required).
|
||||
# * <tt>:billing_address</tt> - The billing address for the cardholder.
|
||||
def authorize(money, credit_card, options = {})
|
||||
request = build_purchase_request(:authorize, money, credit_card, options)
|
||||
commit(:authorize, request)
|
||||
end
|
||||
|
||||
# Authorize and immediately capture funds from a credit card.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be authorized as anInteger value in cents.
|
||||
# * <tt>credit_card</tt> - The CreditCard details for the transaction.
|
||||
# * <tt>options</tt>
|
||||
# * <tt>:order_id</tt> - A unique reference for this order (required).
|
||||
# * <tt>:billing_address</tt> - The billing address for the cardholder.
|
||||
def purchase(money, credit_card, options = {})
|
||||
request = build_purchase_request(:purchase, money, credit_card, options)
|
||||
commit(:purchase, request)
|
||||
end
|
||||
|
||||
# Capture authorized funds from a credit card.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>money</tt> - The amount to be captured as anInteger value in cents.
|
||||
# * <tt>authorization</tt> - The authorization string returned from the initial authorization.
|
||||
def capture(money, authorization, options = {})
|
||||
request = build_capture_request(:capture, money, authorization, options)
|
||||
commit(:capture, request)
|
||||
end
|
||||
|
||||
# Void a transaction.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>authorization</tt> - The authorization string returned from the initial authorization or purchase.
|
||||
def void(authorization, options = {})
|
||||
reference, options[:order_id] = split_reference(authorization)
|
||||
request = v4_soap_request(:void) do |xml|
|
||||
add_reference_token(xml, reference)
|
||||
end
|
||||
commit(:void, request, true)
|
||||
end
|
||||
|
||||
# Refund an amount back a cardholder
|
||||
#
|
||||
# ==== Parameters
|
||||
#
|
||||
# * <tt>money</tt> - The amount to be refunded as an Integer value in cents.
|
||||
# * <tt>identification</tt> - The credit card you want to refund or the authorization for the existing transaction you are refunding.
|
||||
# * <tt>options</tt>
|
||||
# * <tt>:order_id</tt> - A unique reference for this order (required when performing a non-referenced credit)
|
||||
def credit(money, identification, options = {})
|
||||
if identification.is_a?(String)
|
||||
deprecated CREDIT_DEPRECATION_MESSAGE
|
||||
refund(money, identification, options)
|
||||
else
|
||||
perform_credit(money, identification, options)
|
||||
end
|
||||
end
|
||||
|
||||
def refund(money, reference, options = {})
|
||||
perform_reference_credit(money, reference, options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def soap_request(action)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! "env:Envelope", ENV_NAMESPACES do
|
||||
xml.tag! "env:Body" do
|
||||
xml.tag! ACTIONS[action], "xmlns" => TX_NAMESPACE do
|
||||
add_credentials(xml)
|
||||
yield xml
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def v4_soap_request(action)
|
||||
xml = Builder::XmlMarkup.new :indent => 2
|
||||
xml.instruct!
|
||||
xml.tag! "soap:Envelope", ENV_NAMESPACES_V4 do
|
||||
xml.tag! "soap:Body" do
|
||||
xml.tag! ACTIONS[:void], "xmlns" => TX_NAMESPACE_V4 do
|
||||
xml.tag! "merchantName", @options[:name]
|
||||
xml.tag! "merchantSiteId", @options[:login]
|
||||
xml.tag! "merchantKey", @options[:password]
|
||||
yield xml
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_purchase_request(action, money, credit_card, options)
|
||||
requires!(options, :order_id)
|
||||
|
||||
request = soap_request(action) do |xml|
|
||||
add_invoice(xml, options)
|
||||
add_amount(xml, money)
|
||||
add_credit_card(xml, credit_card)
|
||||
add_address(xml, options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_capture_request(action, money, identification, options)
|
||||
reference, options[:order_id] = split_reference(identification)
|
||||
|
||||
request = soap_request(action) do |xml|
|
||||
add_reference(xml, reference)
|
||||
add_invoice(xml, options)
|
||||
add_amount(xml, money)
|
||||
end
|
||||
end
|
||||
|
||||
def perform_reference_credit(money, identification, options)
|
||||
reference, options[:order_id] = split_reference(identification)
|
||||
|
||||
request = soap_request(:reference_credit) do |xml|
|
||||
add_reference(xml, reference)
|
||||
add_invoice(xml, options)
|
||||
add_amount(xml, money, "strOverrideAmount")
|
||||
end
|
||||
|
||||
commit(:reference_credit, request)
|
||||
end
|
||||
|
||||
def perform_credit(money, credit_card, options)
|
||||
requires!(options, :order_id)
|
||||
|
||||
request = soap_request(:credit) do |xml|
|
||||
add_invoice(xml, options)
|
||||
add_amount(xml, money)
|
||||
add_credit_card(xml, credit_card)
|
||||
end
|
||||
|
||||
commit(:credit, request)
|
||||
end
|
||||
|
||||
def add_credentials(xml)
|
||||
xml.tag! "strSiteId", @options[:login]
|
||||
xml.tag! "strKey", @options[:password]
|
||||
xml.tag! "strName", @options[:name]
|
||||
end
|
||||
|
||||
def expdate(credit_card)
|
||||
year = sprintf("%.4i", credit_card.year)
|
||||
month = sprintf("%.2i", credit_card.month)
|
||||
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def add_invoice(xml, options)
|
||||
xml.tag! "strOrderNumber", options[:order_id].to_s.gsub(/[^\w]/, '').slice(0, 25)
|
||||
end
|
||||
|
||||
def add_amount(xml, money, tag = "strAmount")
|
||||
xml.tag! tag, amount(money)
|
||||
end
|
||||
|
||||
def add_reference(xml, reference)
|
||||
xml.tag! "strReferenceCode", reference
|
||||
end
|
||||
|
||||
def add_reference_token(xml, reference)
|
||||
xml.tag! "token", reference
|
||||
end
|
||||
|
||||
def add_address(xml, options)
|
||||
if address = options[:billing_address] || options[:address]
|
||||
xml.tag! "strAVSStreetAddress", address[:address1]
|
||||
xml.tag! "strAVSZipCode", address[:zip]
|
||||
end
|
||||
end
|
||||
|
||||
def add_credit_card(xml, credit_card)
|
||||
xml.tag! "strPAN", credit_card.number
|
||||
xml.tag! "strExpDate", expdate(credit_card)
|
||||
xml.tag! "strCardHolder", credit_card.name
|
||||
xml.tag! "strCVCode", credit_card.verification_value if credit_card.verification_value?
|
||||
end
|
||||
|
||||
def split_reference(reference)
|
||||
reference.to_s.split(";")
|
||||
end
|
||||
|
||||
def parse(action, data)
|
||||
response = {}
|
||||
xml = REXML::Document.new(data)
|
||||
|
||||
root = REXML::XPath.first(xml, "//#{ACTIONS[action]}Response/#{ACTIONS[action]}Result")
|
||||
|
||||
root.elements.each do |element|
|
||||
response[element.name] = element.text
|
||||
end
|
||||
|
||||
status, code, message = response["ApprovalStatus"].split(";")
|
||||
response[:status] = status
|
||||
|
||||
if response[:success] = status == "APPROVED"
|
||||
response[:message] = status
|
||||
else
|
||||
response[:message] = message
|
||||
response[:failure_code] = code
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def parse_error(http_response)
|
||||
response = {}
|
||||
response[:http_code] = http_response.code
|
||||
response[:http_message] = http_response.message
|
||||
response[:success] = false
|
||||
|
||||
document = REXML::Document.new(http_response.body)
|
||||
|
||||
node = REXML::XPath.first(document, "//soap:Fault")
|
||||
|
||||
node.elements.each do |element|
|
||||
response[element.name] = element.text
|
||||
end
|
||||
|
||||
response[:message] = response["faultstring"].to_s.gsub("\n", " ")
|
||||
response
|
||||
rescue REXML::ParseException => e
|
||||
response[:http_body] = http_response.body
|
||||
response[:message] = "Failed to parse the failed response"
|
||||
response
|
||||
end
|
||||
|
||||
def soap_action(action, v4 = false)
|
||||
v4 ? "#{TX_NAMESPACE_V4}#{ACTIONS[action]}" : "#{TX_NAMESPACE}/#{ACTIONS[action]}"
|
||||
end
|
||||
|
||||
def url(v4 = false)
|
||||
v4 ? v4_live_url : live_url
|
||||
end
|
||||
|
||||
def commit(action, request, v4 = false)
|
||||
begin
|
||||
data = ssl_post(url(v4), request,
|
||||
"Content-Type" => 'text/xml; charset=utf-8',
|
||||
"SOAPAction" => soap_action(action, v4)
|
||||
)
|
||||
response = parse(action, data)
|
||||
rescue ActiveMerchant::ResponseError => e
|
||||
response = parse_error(e.response)
|
||||
end
|
||||
|
||||
Response.new(response[:success], response[:message], response,
|
||||
:test => test?,
|
||||
:authorization => authorization_from(response),
|
||||
:avs_result => { :code => response["AVSResponse"] },
|
||||
:cvv_result => response["CVResponse"]
|
||||
)
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
if response[:success]
|
||||
[ response["ReferenceID"], response["OrderNumber"] ].join(";")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
190
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_warrior.rb
vendored
Normal file
190
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/merchant_warrior.rb
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
require 'digest/md5'
|
||||
require 'rexml/document'
|
||||
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class MerchantWarriorGateway < Gateway
|
||||
TOKEN_TEST_URL = 'https://base.merchantwarrior.com/token/'
|
||||
TOKEN_LIVE_URL = 'https://api.merchantwarrior.com/token/'
|
||||
|
||||
POST_TEST_URL = 'https://base.merchantwarrior.com/post/'
|
||||
POST_LIVE_URL = 'https://api.merchantwarrior.com/post/'
|
||||
|
||||
self.supported_countries = ['AU']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express,
|
||||
:diners_club, :discover]
|
||||
self.homepage_url = 'http://www.merchantwarrior.com/'
|
||||
self.display_name = 'MerchantWarrior'
|
||||
|
||||
self.money_format = :dollars
|
||||
self.default_currency = 'AUD'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :merchant_uuid, :api_key, :api_passphrase)
|
||||
super
|
||||
end
|
||||
|
||||
def authorize(money, payment_method, options = {})
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_product(post, options)
|
||||
add_address(post, options)
|
||||
add_payment_method(post, payment_method)
|
||||
commit('processAuth', post)
|
||||
end
|
||||
|
||||
def purchase(money, payment_method, options = {})
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_product(post, options)
|
||||
add_address(post, options)
|
||||
add_payment_method(post, payment_method)
|
||||
commit('processCard', post)
|
||||
end
|
||||
|
||||
def capture(money, identification)
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_transaction(post, identification)
|
||||
post.merge!('captureAmount' => money.to_s)
|
||||
commit('processCapture', post)
|
||||
end
|
||||
|
||||
def refund(money, identification)
|
||||
post = {}
|
||||
add_amount(post, money, options)
|
||||
add_transaction(post, identification)
|
||||
post['refundAmount'] = money
|
||||
commit('refundCard', post)
|
||||
end
|
||||
|
||||
def store(creditcard, options = {})
|
||||
post = {
|
||||
'cardName' => creditcard.name,
|
||||
'cardNumber' => creditcard.number,
|
||||
'cardExpiryMonth' => format(creditcard.month, :two_digits),
|
||||
'cardExpiryYear' => format(creditcard.year, :two_digits)
|
||||
}
|
||||
commit('addCard', post)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_transaction(post, identification)
|
||||
post['transactionID'] = identification
|
||||
end
|
||||
|
||||
def add_address(post, options)
|
||||
return unless(address = options[:address])
|
||||
|
||||
post['customerName'] = address[:name]
|
||||
post['customerCountry'] = address[:country]
|
||||
post['customerState'] = address[:state]
|
||||
post['customerCity'] = address[:city]
|
||||
post['customerAddress'] = address[:address1]
|
||||
post['customerPostCode'] = address[:zip]
|
||||
end
|
||||
|
||||
def add_product(post, options)
|
||||
post['transactionProduct'] = options[:transaction_product]
|
||||
end
|
||||
|
||||
def add_payment_method(post, payment_method)
|
||||
if payment_method.respond_to?(:number)
|
||||
add_creditcard(post, payment_method)
|
||||
else
|
||||
add_token(post, payment_method)
|
||||
end
|
||||
end
|
||||
|
||||
def add_token(post, token)
|
||||
post['cardID'] = token
|
||||
end
|
||||
|
||||
def add_creditcard(post, creditcard)
|
||||
post['paymentCardNumber'] = creditcard.number
|
||||
post['paymentCardName'] = creditcard.name
|
||||
post['paymentCardExpiry'] = creditcard.expiry_date.expiration.strftime("%m%y")
|
||||
end
|
||||
|
||||
def add_amount(post, money, options)
|
||||
currency = (options[:currency] || currency(money))
|
||||
|
||||
post['transactionAmount'] = money.to_s
|
||||
post['transactionCurrency'] = currency
|
||||
post['hash'] = verification_hash(money, currency)
|
||||
end
|
||||
|
||||
def verification_hash(money, currency)
|
||||
Digest::MD5.hexdigest(
|
||||
(
|
||||
@options[:api_passphrase].to_s +
|
||||
@options[:merchant_uuid].to_s +
|
||||
money.to_s +
|
||||
currency
|
||||
).downcase
|
||||
)
|
||||
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
|
||||
|
||||
def commit(action, post)
|
||||
add_auth(action, post)
|
||||
|
||||
response = parse(ssl_post(url_for(action, post), post_data(post)))
|
||||
|
||||
Response.new(
|
||||
success?(response),
|
||||
response[:response_message],
|
||||
response,
|
||||
:test => test?,
|
||||
:authorization => (response[:card_id] || response[:transaction_id])
|
||||
)
|
||||
end
|
||||
|
||||
def add_auth(action, post)
|
||||
post['merchantUUID'] = @options[:merchant_uuid]
|
||||
post['apiKey'] = @options[:api_key]
|
||||
unless token?(post)
|
||||
post['method'] = action
|
||||
end
|
||||
end
|
||||
|
||||
def url_for(action, post)
|
||||
if token?(post)
|
||||
[(test? ? TOKEN_TEST_URL : TOKEN_LIVE_URL), action].join("/")
|
||||
else
|
||||
(test? ? POST_TEST_URL : POST_LIVE_URL)
|
||||
end
|
||||
end
|
||||
|
||||
def token?(post)
|
||||
(post["cardID"] || post["cardName"])
|
||||
end
|
||||
|
||||
def success?(response)
|
||||
(response[:response_code] == '0')
|
||||
end
|
||||
|
||||
def post_data(post)
|
||||
post.collect{|k,v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
272
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/mercury.rb
vendored
Normal file
272
vendor/gems/activemerchant-1.33.0/lib/active_merchant/billing/gateways/mercury.rb
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
module ActiveMerchant #:nodoc:
|
||||
module Billing #:nodoc:
|
||||
class MercuryGateway < Gateway
|
||||
URLS = {
|
||||
:test => 'https://w1.mercurydev.net/ws/ws.asmx',
|
||||
:live => 'https://w1.mercurypay.com/ws/ws.asmx'
|
||||
}
|
||||
|
||||
self.homepage_url = 'http://www.mercurypay.com'
|
||||
self.display_name = 'Mercury'
|
||||
self.supported_countries = ['US']
|
||||
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
|
||||
self.default_currency = 'USD'
|
||||
|
||||
def initialize(options = {})
|
||||
requires!(options, :login, :password)
|
||||
super
|
||||
end
|
||||
|
||||
def purchase(money, credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
request = build_non_authorized_request('Sale', money, credit_card, options)
|
||||
commit('Sale', request)
|
||||
end
|
||||
|
||||
def credit(money, credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
request = build_non_authorized_request('Return', money, credit_card, options)
|
||||
commit('Return', request)
|
||||
end
|
||||
|
||||
def authorize(money, credit_card, options = {})
|
||||
requires!(options, :order_id)
|
||||
|
||||
options[:authorized] ||= money
|
||||
request = build_non_authorized_request('PreAuth', money, credit_card, options)
|
||||
commit('PreAuth', request)
|
||||
end
|
||||
|
||||
def capture(money, authorization, options = {})
|
||||
requires!(options, :credit_card)
|
||||
options[:authorized] ||= money
|
||||
request = build_authorized_request('PreAuthCapture', money, authorization, options[:credit_card], options)
|
||||
commit('PreAuthCapture', request)
|
||||
end
|
||||
|
||||
def refund(money, authorization, options = {})
|
||||
requires!(options, :credit_card)
|
||||
|
||||
request = build_authorized_request('VoidSale', money, authorization, options[:credit_card], options)
|
||||
commit(options[:void], request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_non_authorized_request(action, money, credit_card, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
xml.tag! "TStream" do
|
||||
xml.tag! "Transaction" do
|
||||
xml.tag! 'TranType', 'Credit'
|
||||
xml.tag! 'TranCode', action
|
||||
if action == 'PreAuth' || action == 'Sale'
|
||||
xml.tag! "PartialAuth", "Allow"
|
||||
end
|
||||
add_invoice(xml, options[:order_id], nil, options)
|
||||
add_customer_data(xml, options)
|
||||
add_amount(xml, money, options)
|
||||
add_credit_card(xml, credit_card, action)
|
||||
add_address(xml, options)
|
||||
end
|
||||
end
|
||||
xml = xml.target!
|
||||
end
|
||||
|
||||
def build_authorized_request(action, money, authorization, credit_card, options)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
invoice_no, ref_no, auth_code, acq_ref_data, process_data = split_authorization(authorization)
|
||||
|
||||
xml.tag! "TStream" do
|
||||
xml.tag! "Transaction" do
|
||||
xml.tag! 'TranType', 'Credit'
|
||||
xml.tag! 'TranCode', action
|
||||
if action == 'PreAuthCapture'
|
||||
xml.tag! "PartialAuth", "Allow"
|
||||
end
|
||||
add_invoice(xml, invoice_no, ref_no, options)
|
||||
add_customer_data(xml, options)
|
||||
add_amount(xml, money, options)
|
||||
add_credit_card(xml, credit_card, action)
|
||||
add_address(xml, options)
|
||||
xml.tag! 'TranInfo' do
|
||||
xml.tag! "AuthCode", auth_code
|
||||
xml.tag! "AcqRefData", acq_ref_data
|
||||
xml.tag! "ProcessData", process_data
|
||||
end
|
||||
end
|
||||
end
|
||||
xml = xml.target!
|
||||
end
|
||||
|
||||
def add_invoice(xml, invoice_no, ref_no, options)
|
||||
if /^\d+$/ !~ invoice_no.to_s
|
||||
raise ArgumentError.new("#{invoice_no} is not numeric as required by Mercury")
|
||||
end
|
||||
|
||||
xml.tag! 'InvoiceNo', invoice_no
|
||||
xml.tag! 'RefNo', ref_no || invoice_no
|
||||
xml.tag! 'OperatorID', options[:merchant] if options[:merchant]
|
||||
xml.tag! 'Memo', options[:description] if options[:description]
|
||||
end
|
||||
|
||||
def add_customer_data(xml, options)
|
||||
xml.tag! 'IpAddress', options[:ip] if options[:ip]
|
||||
if options[:customer]
|
||||
xml.tag! "TranInfo" do
|
||||
xml.tag! 'CustomerCode', options[:customer]
|
||||
end
|
||||
end
|
||||
xml.tag! 'MerchantID', @options[:login]
|
||||
end
|
||||
|
||||
def add_amount(xml, money, options = {})
|
||||
xml.tag! 'Amount' do
|
||||
xml.tag! 'Purchase', amount(money)
|
||||
xml.tag! 'Tax', options[:tax] if options[:tax]
|
||||
xml.tag! 'Authorize', amount(options[:authorized]) if options[:authorized]
|
||||
xml.tag! 'Gratuity', amount(options[:tip]) if options[:tip]
|
||||
end
|
||||
end
|
||||
|
||||
CARD_CODES = {
|
||||
'visa' => 'VISA',
|
||||
'master' => 'M/C',
|
||||
'american_express' => 'AMEX',
|
||||
'discover' => 'DCVR',
|
||||
'diners_club' => 'DCLB',
|
||||
'jcb' => 'JCB'
|
||||
}
|
||||
|
||||
def add_credit_card(xml, credit_card, action)
|
||||
xml.tag! 'Account' do
|
||||
xml.tag! 'AcctNo', credit_card.number
|
||||
xml.tag! 'ExpDate', expdate(credit_card)
|
||||
end
|
||||
xml.tag! 'CardType', CARD_CODES[credit_card.brand] if credit_card.brand
|
||||
|
||||
include_cvv = !%w(Return PreAuthCapture).include?(action)
|
||||
xml.tag! 'CVVData', credit_card.verification_value if(include_cvv && credit_card.verification_value)
|
||||
end
|
||||
|
||||
def expdate(credit_card)
|
||||
year = sprintf("%.4i", credit_card.year)
|
||||
month = sprintf("%.2i", credit_card.month)
|
||||
|
||||
"#{month}#{year[-2..-1]}"
|
||||
end
|
||||
|
||||
def add_address(xml, options)
|
||||
if billing_address = options[:billing_address] || options[:address]
|
||||
xml.tag! 'AVS' do
|
||||
xml.tag! 'Address', billing_address[:address1]
|
||||
xml.tag! 'Zip', billing_address[:zip]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse(action, body)
|
||||
response = {}
|
||||
hashify_xml!(unescape_xml(body), response)
|
||||
response
|
||||
end
|
||||
|
||||
def hashify_xml!(xml, response)
|
||||
xml = REXML::Document.new(xml)
|
||||
|
||||
xml.elements.each("//CmdResponse/*") do |node|
|
||||
response[node.name.underscore.to_sym] = node.text
|
||||
end
|
||||
|
||||
xml.elements.each("//TranResponse/*") do |node|
|
||||
if node.name.to_s == "Amount"
|
||||
node.elements.each do |amt|
|
||||
response[amt.name.underscore.to_sym] = amt.text
|
||||
end
|
||||
else
|
||||
response[node.name.underscore.to_sym] = node.text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def endpoint_url
|
||||
URLS[test? ? :test : :live]
|
||||
end
|
||||
|
||||
def build_soap_request(body)
|
||||
xml = Builder::XmlMarkup.new
|
||||
|
||||
xml.instruct!
|
||||
xml.tag! 'soap:Envelope', ENVELOPE_NAMESPACES do
|
||||
xml.tag! 'soap:Body' do
|
||||
xml.tag! 'CreditTransaction', 'xmlns' => homepage_url do
|
||||
xml.tag! 'tran' do
|
||||
xml << escape_xml(body)
|
||||
end
|
||||
xml.tag! 'pw', @options[:password]
|
||||
end
|
||||
end
|
||||
end
|
||||
xml.target!
|
||||
end
|
||||
|
||||
def build_header
|
||||
{
|
||||
"SOAPAction" => "http://www.mercurypay.com/CreditTransaction",
|
||||
"Content-Type" => "text/xml; charset=utf-8"
|
||||
}
|
||||
end
|
||||
|
||||
SUCCESS_CODES = [ 'Approved', 'Success' ]
|
||||
|
||||
def commit(action, request)
|
||||
response = parse(action, ssl_post(endpoint_url, build_soap_request(request), build_header))
|
||||
|
||||
success = SUCCESS_CODES.include?(response[:cmd_status])
|
||||
message = success ? 'Success' : message_from(response)
|
||||
|
||||
Response.new(success, message, response,
|
||||
:test => test?,
|
||||
:authorization => authorization_from(response),
|
||||
:avs_result => { :code => response[:avs_result] },
|
||||
:cvv_result => response[:cvv_result])
|
||||
end
|
||||
|
||||
def message_from(response)
|
||||
response[:text_response]
|
||||
end
|
||||
|
||||
def authorization_from(response)
|
||||
[
|
||||
response[:invoice_no],
|
||||
response[:ref_no],
|
||||
response[:auth_code],
|
||||
response[:acq_ref_data],
|
||||
response[:process_data]
|
||||
].join(";")
|
||||
end
|
||||
|
||||
def split_authorization(authorization)
|
||||
invoice_no, ref_no, auth_code, acq_ref_data, process_data = authorization.split(";")
|
||||
[invoice_no, ref_no, auth_code, acq_ref_data, process_data]
|
||||
end
|
||||
|
||||
ENVELOPE_NAMESPACES = {
|
||||
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
||||
'xmlns:soap' => "http://schemas.xmlsoap.org/soap/envelope/",
|
||||
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
||||
}
|
||||
|
||||
def escape_xml(xml)
|
||||
"\n<![CDATA[\n#{xml}\n]]>\n"
|
||||
end
|
||||
|
||||
def unescape_xml(escaped_xml)
|
||||
escaped_xml.gsub(/\>/,'>').gsub(/\</,'<')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user