ipay 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +76 -0
- data/Rakefile +13 -0
- data/example/certification.rb +31 -0
- data/lib/console.rb +2 -0
- data/lib/ipay.rb +42 -0
- data/lib/ipay/api_request.rb +43 -0
- data/lib/ipay/cc.rb +49 -0
- data/lib/ipay/certification.rb +35 -0
- data/lib/ipay/config.rb +35 -0
- data/lib/ipay/countries.rb +7 -0
- data/lib/ipay/log.rb +28 -0
- data/lib/ipay/network.rb +13 -0
- data/lib/ipay/response.rb +67 -0
- data/lib/ipay/util.rb +29 -0
- data/lib/ipay/version.rb +3 -0
- data/lib/ipay/wallet.rb +44 -0
- data/lib/ipay/xml_request.rb +69 -0
- data/test/helper.rb +13 -0
- data/test/test_cc.rb +126 -0
- data/test/test_errors.rb +47 -0
- data/test/test_wallet.rb +199 -0
- metadata +86 -0
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
iPay
|
2
|
+
======
|
3
|
+
|
4
|
+
Ruby gem for interfacing with the iPay XML API
|
5
|
+
|
6
|
+
Changelog
|
7
|
+
---------
|
8
|
+
|
9
|
+
**v0.1.1**
|
10
|
+
|
11
|
+
- Added certification mode
|
12
|
+
|
13
|
+
**v0.1.0**
|
14
|
+
|
15
|
+
- CC (credit/debit) and Wallet (client/account) requests
|
16
|
+
- Tests
|
17
|
+
|
18
|
+
**v0.0.1**
|
19
|
+
|
20
|
+
- Initial commit
|
21
|
+
|
22
|
+
Dependencies
|
23
|
+
----
|
24
|
+
|
25
|
+
- libxml-ruby for processing xml responses
|
26
|
+
|
27
|
+
Setup
|
28
|
+
-----
|
29
|
+
|
30
|
+
Create a configuration file 'ipay.yml' and place it in your apps config/ folder. The configuration file is automatically loaded for you and values are accessible via iPay::config struct.
|
31
|
+
|
32
|
+
Example configuration:
|
33
|
+
|
34
|
+
:url: "https://uap.txngw.com/"
|
35
|
+
:company_key: 6990
|
36
|
+
:terminal_id: 6177
|
37
|
+
:pin: 1234
|
38
|
+
|
39
|
+
You can also configure IPay via a block:
|
40
|
+
|
41
|
+
IPay::config do |c|
|
42
|
+
c.url = 'https://uap.txngw.com/'
|
43
|
+
c.company_key = 6990
|
44
|
+
c.terminal_id = 6177
|
45
|
+
c.pin = 1234
|
46
|
+
end
|
47
|
+
|
48
|
+
Usage
|
49
|
+
----
|
50
|
+
require 'ipay'
|
51
|
+
resp = IPay::CC::Debit.sale(
|
52
|
+
:amount => '4.99',
|
53
|
+
:account_number => '4000009999999991',
|
54
|
+
:cvv => 123,
|
55
|
+
:expiration => '0614',
|
56
|
+
:first_name => 'nick',
|
57
|
+
:last_name => 'wilson',
|
58
|
+
:address => '123 fake st',
|
59
|
+
:city => 'sometown',
|
60
|
+
:state => 'NY',
|
61
|
+
:postal_code => '90210',
|
62
|
+
:country => IPay::Countries::USA
|
63
|
+
)
|
64
|
+
|
65
|
+
if resp.success?
|
66
|
+
puts resp.data[:transaction_id]
|
67
|
+
end
|
68
|
+
|
69
|
+
Certification
|
70
|
+
----
|
71
|
+
|
72
|
+
IPay requires that test accounts submit an xml file of compiled responses before allowing an account to be used in production mode. The IPay gem has a certification mode that will compile all responses into the appropriate file/format for you automatically:
|
73
|
+
|
74
|
+
IPay::Certification.capture do
|
75
|
+
# ... all responses for api requests are now logged and saved when the block ends
|
76
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
task :default => [:test]
|
4
|
+
|
5
|
+
task :console do
|
6
|
+
sh 'irb --simple-prompt -rubygems -I lib -r console.rb'
|
7
|
+
end
|
8
|
+
|
9
|
+
Rake::TestTask.new('test') do |t|
|
10
|
+
t.libs << 'test'
|
11
|
+
t.test_files = FileList['test/test*.rb']
|
12
|
+
t.verbose = true
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
3
|
+
|
4
|
+
require 'ipay'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
CC_EXP = "#{Date.today.month.to_s.rjust(2,'0')}#{Date.today.year.to_s[2..-1]}"
|
8
|
+
|
9
|
+
IPay::log = STDOUT
|
10
|
+
|
11
|
+
IPay::config do |c|
|
12
|
+
c.url = 'https://uap.txngw.com/'
|
13
|
+
c.company_key = 6990
|
14
|
+
c.terminal_id = 6177
|
15
|
+
c.pin = 1234
|
16
|
+
end
|
17
|
+
|
18
|
+
IPay::Certification.capture do
|
19
|
+
|
20
|
+
resp = IPay::CC::Debit.sale(
|
21
|
+
:amount => '4.99',
|
22
|
+
:account_number => '4000009999999991', :cvv => 123, :expiration => CC_EXP,
|
23
|
+
:first_name => 'nick', :last_name => 'wilson', :address => '123 fake st', :city => 'sometown', :state => 'NY', :postal_code => '90210', :country => IPay::Countries::USA
|
24
|
+
)
|
25
|
+
|
26
|
+
resp = IPay::CC::Debit.sale(
|
27
|
+
:amount => '59.99',
|
28
|
+
:account_number => '4000009999999991', :cvv => 123, :expiration => CC_EXP,
|
29
|
+
:first_name => 'nick', :last_name => 'wilson', :address => '123 fake st', :city => 'sometown', :state => 'NY', :postal_code => '90210', :country => IPay::Countries::USA
|
30
|
+
)
|
31
|
+
end
|
data/lib/console.rb
ADDED
data/lib/ipay.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module IPay
|
2
|
+
ENV = ENV['RAILS_ENV'] || ENV['IPAY_ENV'] || 'development'
|
3
|
+
ROOT = ENV['RAILS_ROOT'] || ENV['IPAY_ROOT'] || '.'
|
4
|
+
CONFIG_NAME = 'ipay.yml'
|
5
|
+
LOG_NAME = 'ipay.log'
|
6
|
+
|
7
|
+
EM_SWIPED = 1
|
8
|
+
EM_MANUAL_PRESENT = 2
|
9
|
+
EM_MANUAL_NOT_PRESENT = 3
|
10
|
+
|
11
|
+
GOODS_DIGITAL = 'D'
|
12
|
+
GOODS_PHYSICAL = 'P'
|
13
|
+
|
14
|
+
CUR_DOMESTIC = 0
|
15
|
+
CUR_MCP = 1
|
16
|
+
CUR_PYC = 2
|
17
|
+
|
18
|
+
TXN_VIA_MAIL = 'M'
|
19
|
+
TXN_VIA_POS = 'P'
|
20
|
+
TXN_VIA_PHONE = 'T'
|
21
|
+
TXN_VIA_RECUR = 2
|
22
|
+
TXN_AUTHENTICATED = 5
|
23
|
+
TXN_AUTH_FAILED = 6
|
24
|
+
TXN_VIA_HTTPS = 7
|
25
|
+
|
26
|
+
ApiError = Class.new(RuntimeError)
|
27
|
+
RequestError = Class.new(ApiError)
|
28
|
+
RequestTimeout = Class.new(RequestError)
|
29
|
+
ResponseError = Class.new(ApiError)
|
30
|
+
|
31
|
+
|
32
|
+
autoload :CC, 'ipay/cc'
|
33
|
+
autoload :Wallet, 'ipay/wallet'
|
34
|
+
autoload :Network, 'ipay/network'
|
35
|
+
|
36
|
+
autoload :Certification, 'ipay/certification'
|
37
|
+
end
|
38
|
+
|
39
|
+
$:.unshift(File.dirname(__FILE__))
|
40
|
+
%w[ version config log countries ].each do |file|
|
41
|
+
require "ipay/#{file}"
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'ipay/xml_request'
|
6
|
+
require 'ipay/response'
|
7
|
+
|
8
|
+
module IPay
|
9
|
+
class ApiRequest
|
10
|
+
|
11
|
+
DEFAULT_SERVICE_FORMAT = '0000'
|
12
|
+
|
13
|
+
def self.service_format
|
14
|
+
@@service_format ||= DEFAULT_SERVICE_FORMAT
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.service_format=(val)
|
18
|
+
@@service_format = val
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.service
|
22
|
+
self.name.split('::')[1] rescue nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.service_type
|
26
|
+
self.name.split('::')[2] rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.send_request(data = {}, service_subtype = nil)
|
30
|
+
data[:service] = self.service.upcase
|
31
|
+
data[:service_type] = self.service_type.upcase
|
32
|
+
data[:service_subtype] = service_subtype.nil? ? caller[0][/`.*'/][1..-2].upcase : service_subtype.to_s.upcase
|
33
|
+
data[:service_format] ||= self.service_format
|
34
|
+
|
35
|
+
m = eval("#{self.service}")
|
36
|
+
data = m::default_values(data) if m::respond_to?(:default_values)
|
37
|
+
|
38
|
+
request = XmlRequest.new(data)
|
39
|
+
Response.new request.send
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/lib/ipay/cc.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'ipay/api_request'
|
2
|
+
|
3
|
+
module IPay
|
4
|
+
module CC
|
5
|
+
|
6
|
+
def self.default_values(data)
|
7
|
+
data[:currency_code] ||= 840 # USD
|
8
|
+
data[:currency_indicator] ||= CUR_DOMESTIC
|
9
|
+
data[:transaction_indicator] ||= TXN_VIA_HTTPS
|
10
|
+
data
|
11
|
+
end
|
12
|
+
|
13
|
+
class Credit < ApiRequest
|
14
|
+
self.service_format = '1010'
|
15
|
+
|
16
|
+
def self.refund(data)
|
17
|
+
self.send_request(data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.void(data)
|
21
|
+
self.send_request(data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Debit < ApiRequest
|
26
|
+
self.service_format = '1010'
|
27
|
+
|
28
|
+
def self.auth(data)
|
29
|
+
data[:goods_indicator] ||= GOODS_DIGITAL
|
30
|
+
self.send_request(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.capture(data)
|
34
|
+
self.send_request(data)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.sale(data)
|
38
|
+
data[:goods_indicator] ||= GOODS_DIGITAL
|
39
|
+
data[:entry_mode] ||= EM_MANUAL_NOT_PRESENT
|
40
|
+
self.send_request(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.void(data)
|
44
|
+
self.send_request(data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module IPay
|
4
|
+
module Certification
|
5
|
+
|
6
|
+
def self.log(parsed_xml)
|
7
|
+
@@responses ||= ''
|
8
|
+
@@responses << parsed_xml.to_s.split("\n")[2...-1].join("\n") + "\n"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.capture(output_path = './')
|
12
|
+
IPay::config do |c|
|
13
|
+
c.certification = true
|
14
|
+
end
|
15
|
+
|
16
|
+
yield
|
17
|
+
|
18
|
+
self.save(output_path)
|
19
|
+
|
20
|
+
IPay::config do |c|
|
21
|
+
c.certification = false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.save(path)
|
26
|
+
raise 'Certification mode is not activated' unless IPay::config.certification
|
27
|
+
|
28
|
+
file_name = File.join(path, "#{IPay::config.operator}_#{Date.today.to_s.split('-').join}#{Random.new.rand(1..99).to_s.rjust(2, '0')}.xml")
|
29
|
+
file = File.open(file_name, 'w')
|
30
|
+
raise "Failed to open certification file '#{file_name}' for writing" unless file
|
31
|
+
file.write("<RESPONSES>\n#{@@responses}</RESPONSES>")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/ipay/config.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module IPay
|
5
|
+
|
6
|
+
def self.config_file
|
7
|
+
File.expand_path(File.join(ROOT, 'config', CONFIG_NAME))
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.load_config_file
|
11
|
+
path = File.expand_path(config_file)
|
12
|
+
|
13
|
+
if File.readable?(path)
|
14
|
+
IPay::log.debug "Using configuration file '#{path}'"
|
15
|
+
@config = OpenStruct.new(YAML.load_file(path)) unless @config
|
16
|
+
else
|
17
|
+
@config = OpenStruct.new
|
18
|
+
IPay::log.warn "Failed to locate configuration file '#{CONFIG_NAME}', place in 'config/'"
|
19
|
+
end
|
20
|
+
|
21
|
+
set_defaults(@config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.set_defaults(config)
|
25
|
+
config.dry_run ||= false
|
26
|
+
config.certification ||= false
|
27
|
+
config
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.config
|
31
|
+
@config ||= load_config_file
|
32
|
+
yield @config if block_given?
|
33
|
+
@config
|
34
|
+
end
|
35
|
+
end
|
data/lib/ipay/log.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module IPay
|
3
|
+
class Log < Logger
|
4
|
+
def format_message(severity, timestamp, progname, msg)
|
5
|
+
"#{timestamp} #{severity} -- #{msg}\n"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.log_file
|
10
|
+
path = File.join(ROOT, 'log')
|
11
|
+
path = '.' unless File.directory?(path)
|
12
|
+
File.expand_path(File.join(path, LOG_NAME))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.init_log(log = nil)
|
16
|
+
@log = Log.new(log||log_file)
|
17
|
+
@log.level = Logger::WARN if ENV == 'production'
|
18
|
+
@log
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.log
|
22
|
+
@log ||= init_log
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.log=(log)
|
26
|
+
init_log log
|
27
|
+
end
|
28
|
+
end
|
data/lib/ipay/network.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'xml'
|
2
|
+
require 'date'
|
3
|
+
require 'ipay/util'
|
4
|
+
|
5
|
+
module IPay
|
6
|
+
class Response
|
7
|
+
|
8
|
+
PARSER_OPT = XML::Parser::Options::NOBLANKS | XML::Parser::Options::NOERROR | XML::Parser::Options::RECOVER | XML::Parser::Options::NOWARNING
|
9
|
+
|
10
|
+
attr_reader :status, :server_time, :data, :raw_xml
|
11
|
+
|
12
|
+
def initialize(xml)
|
13
|
+
if IPay::config.dry_run
|
14
|
+
@status = {:arc => '00', :mrc => '00', :description => 'Dry Run, no response processed'}
|
15
|
+
else
|
16
|
+
@raw_xml = xml
|
17
|
+
parse_response @raw_xml
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def success?
|
22
|
+
@status[:arc] == '00' && @status[:mrc] == '00'
|
23
|
+
end
|
24
|
+
|
25
|
+
def error?
|
26
|
+
not success?
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse_response(xml)
|
32
|
+
IPay::log.debug 'Parsing response xml...'
|
33
|
+
parser = XML::Parser.string xml
|
34
|
+
parser.context.options = PARSER_OPT
|
35
|
+
parsed = parser.parse
|
36
|
+
|
37
|
+
IPay::Certification.log(parsed) if IPay::config.certification
|
38
|
+
|
39
|
+
response = xml_node_to_hash(parsed.find('//RESPONSE/RESPONSE/FIELDS')[0])
|
40
|
+
raise ResponseError.new 'Invalid response from server' unless response and response.include? :arc
|
41
|
+
|
42
|
+
d = response.delete(:local_date).match(/([0-9]{2})([0-9]{2})([0-9]{4})/)
|
43
|
+
t = response.delete(:local_time).match(/([0-9]{2})([0-9]{2})([0-9]{2})/)
|
44
|
+
|
45
|
+
@server_time = DateTime.parse("#{d[3]}-#{d[1]}-#{d[2]}T#{t[1]}:#{t[2]}:#{t[3]}") rescue DateTime.now
|
46
|
+
@status = { :arc => response.delete(:arc), :mrc => response.delete(:mrc), :description => response.delete(:response_text) }
|
47
|
+
@data = response
|
48
|
+
|
49
|
+
IPay::log.info "ARC=#{@status[:arc]}, MRC=#{@status[:mrc]}, RESPONSE_TEXT=#{@status[:description]}"
|
50
|
+
IPay::log.debug response
|
51
|
+
raise RequestTimeout.new(@status[:description]) if @status[:arc] == 'TO'
|
52
|
+
end
|
53
|
+
|
54
|
+
def xml_node_to_hash(node)
|
55
|
+
result = {}
|
56
|
+
if node.element? && node.children? && node.children[0].element?
|
57
|
+
node.children.each do |child|
|
58
|
+
result[child.name.downcase.to_sym] = xml_node_to_hash(child)
|
59
|
+
end
|
60
|
+
result
|
61
|
+
else
|
62
|
+
Util.unescape(node.content.strip)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/ipay/util.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module IPay
|
2
|
+
module Util
|
3
|
+
|
4
|
+
ESCAPE_CHARS = { '&' => '&', '"' => '"', '<' => '<', '>' => '>', "'" => ''' }
|
5
|
+
|
6
|
+
def self.escape(string)
|
7
|
+
string.to_s.gsub(Regexp.new(ESCAPE_CHARS.keys.join('|')), ESCAPE_CHARS)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.unescape(string)
|
11
|
+
string.gsub(Regexp.new(ESCAPE_CHARS.values.join('|')), ESCAPE_CHARS.invert)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.hash_to_xml(h, l=0)
|
15
|
+
return h.to_s unless h.is_a? Hash
|
16
|
+
xml = ''
|
17
|
+
h.each do |k,v|
|
18
|
+
k = k.to_s.upcase
|
19
|
+
xml << if v.kind_of? Enumerable
|
20
|
+
(v.is_a?(Array) ? v : Array[v]).collect { |group| "#{"\t"*l}<#{k}>\n#{hash_to_xml(group, l+1)}#{"\t"*l}</#{k}>\n"}.join
|
21
|
+
else
|
22
|
+
"#{"\t"*l}<#{k}>#{self.escape(v)}</#{k}>\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
xml
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/ipay/version.rb
ADDED
data/lib/ipay/wallet.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ipay/api_request'
|
2
|
+
|
3
|
+
module IPay
|
4
|
+
module Wallet
|
5
|
+
|
6
|
+
def self.default_values(data)
|
7
|
+
data[:transaction_indicator] ||= TXN_VIA_HTTPS
|
8
|
+
data
|
9
|
+
end
|
10
|
+
|
11
|
+
class Client < ApiRequest
|
12
|
+
self.service_format = '1010'
|
13
|
+
|
14
|
+
def self.insert(data)
|
15
|
+
self.send_request(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.modify(data)
|
19
|
+
self.send_request(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.delete(data)
|
23
|
+
self.send_request(data)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Account < ApiRequest
|
28
|
+
self.service_format = '1010'
|
29
|
+
|
30
|
+
def self.insert(data)
|
31
|
+
self.send_request(data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.modify(data)
|
35
|
+
self.send_request(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.delete(data)
|
39
|
+
self.send_request(data)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'yaml'
|
4
|
+
require 'ipay/util'
|
5
|
+
|
6
|
+
module IPay
|
7
|
+
class XmlRequest
|
8
|
+
|
9
|
+
CONFIG_GLOBALS = [:operator, :terminal_id, :pin, :verbose_response]
|
10
|
+
|
11
|
+
def initialize(data = {})
|
12
|
+
@data = data
|
13
|
+
IPay::log.debug "Request Args: #{(@data.collect { |k, v| "#{k}=#{v}" }.join(','))}"
|
14
|
+
|
15
|
+
CONFIG_GLOBALS.each do |key|
|
16
|
+
next if @data.include?(key)
|
17
|
+
@data[key] = IPay::config.send(key) if IPay::config.respond_to?(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
@xml = build_xml Util::hash_to_xml(@data)
|
21
|
+
IPay::log.debug @xml
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@xml
|
26
|
+
end
|
27
|
+
|
28
|
+
def send
|
29
|
+
do_post(IPay::config.url, @xml)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def do_post(api_url, data)
|
35
|
+
IPay::log.debug "POST to #{api_url}"
|
36
|
+
|
37
|
+
if IPay::config.dry_run
|
38
|
+
IPay::log.info 'Dry run enabled, not sending to API'
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
url = URI.parse(api_url)
|
43
|
+
http = Net::HTTP.new(url.host, url.port)
|
44
|
+
http.use_ssl = true
|
45
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
46
|
+
store = OpenSSL::X509::Store.new
|
47
|
+
store.set_default_paths
|
48
|
+
http.cert_store = store
|
49
|
+
|
50
|
+
req = Net::HTTP::Post.new(url.path)
|
51
|
+
req.body = data
|
52
|
+
|
53
|
+
res = http.start { |http| http.request(req) }
|
54
|
+
res.body
|
55
|
+
|
56
|
+
rescue EOFError
|
57
|
+
raise RequestError.new('Unable to send your request or the request was rejected by the server.')
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_xml(fields_xml)
|
61
|
+
"<REQUEST KEY=\"#{IPay::config.company_key}\" PROTOCOL=\"1\" FMT=\"1\" ENCODING=\"0\">
|
62
|
+
<TRANSACTION>
|
63
|
+
<FIELDS>#{fields_xml}</FIELDS>
|
64
|
+
</TRANSACTION>
|
65
|
+
</REQUEST>"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ipay'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
class Test::Unit::TestCase
|
6
|
+
def self.test(string, &block)
|
7
|
+
define_method("test:#{string}", &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def assert_not(expression)
|
11
|
+
assert_block("Expected <#{expression}> to be false!") { not expression }
|
12
|
+
end
|
13
|
+
end
|
data/test/test_cc.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestCC < Test::Unit::TestCase
|
4
|
+
CC_EXP = "#{Date.today.month.to_s.rjust(2,'0')}#{Date.today.year.to_s[2..-1]}"
|
5
|
+
|
6
|
+
test 'debit sale' do
|
7
|
+
resp = IPay::CC::Debit.sale(
|
8
|
+
:amount => '4.99',
|
9
|
+
:account_number => '4000009999999991',
|
10
|
+
:cvv => 123,
|
11
|
+
:expiration => CC_EXP,
|
12
|
+
:first_name => 'nick',
|
13
|
+
:last_name => 'wilson',
|
14
|
+
:address => '123 fake st',
|
15
|
+
:city => 'sometown',
|
16
|
+
:state => 'NY',
|
17
|
+
:postal_code => '90210',
|
18
|
+
:country => IPay::Countries::USA
|
19
|
+
)
|
20
|
+
|
21
|
+
assert resp.success?
|
22
|
+
assert resp.data.include?(:transaction_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'debit capture and auth' do
|
26
|
+
resp = IPay::CC::Debit.auth(
|
27
|
+
:amount => '4.99',
|
28
|
+
:account_number => '4000009999999991',
|
29
|
+
:cvv => 123,
|
30
|
+
:expiration => CC_EXP,
|
31
|
+
:first_name => 'nick',
|
32
|
+
:last_name => 'wilson',
|
33
|
+
:address => '123 fake st',
|
34
|
+
:city => 'sometown',
|
35
|
+
:state => 'NY',
|
36
|
+
:postal_code => '90210',
|
37
|
+
:country => IPay::Countries::USA
|
38
|
+
)
|
39
|
+
|
40
|
+
assert resp.success?
|
41
|
+
assert resp.data.include?(:transaction_id)
|
42
|
+
|
43
|
+
sleep(3)
|
44
|
+
|
45
|
+
resp = IPay::CC::Debit.capture(
|
46
|
+
:amount => '4.99',
|
47
|
+
:transaction_id => resp.data[:transaction_id]
|
48
|
+
)
|
49
|
+
|
50
|
+
assert resp.success?
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'debit void' do
|
54
|
+
resp = IPay::CC::Debit.sale(
|
55
|
+
:amount => '4.99',
|
56
|
+
:account_number => '4000009999999991',
|
57
|
+
:cvv => 123,
|
58
|
+
:expiration => CC_EXP,
|
59
|
+
:first_name => 'nick',
|
60
|
+
:last_name => 'wilson',
|
61
|
+
:address => '123 fake st',
|
62
|
+
:city => 'sometown',
|
63
|
+
:state => 'NY',
|
64
|
+
:postal_code => '90210',
|
65
|
+
:country => IPay::Countries::USA
|
66
|
+
)
|
67
|
+
|
68
|
+
assert resp.success?
|
69
|
+
assert resp.data.include?(:transaction_id)
|
70
|
+
|
71
|
+
sleep(3)
|
72
|
+
|
73
|
+
resp = IPay::CC::Debit.void(
|
74
|
+
:transaction_id => resp.data[:transaction_id]
|
75
|
+
)
|
76
|
+
|
77
|
+
assert resp.success?
|
78
|
+
end
|
79
|
+
|
80
|
+
test 'credit refund' do
|
81
|
+
resp = IPay::CC::Credit.refund(
|
82
|
+
:amount => '4.99',
|
83
|
+
:account_number => '4000009999999991',
|
84
|
+
:cvv => 123,
|
85
|
+
:expiration => CC_EXP,
|
86
|
+
:first_name => 'nick',
|
87
|
+
:last_name => 'wilson',
|
88
|
+
:address => '123 fake st',
|
89
|
+
:city => 'sometown',
|
90
|
+
:state => 'NY',
|
91
|
+
:postal_code => '90210',
|
92
|
+
:country => IPay::Countries::USA
|
93
|
+
)
|
94
|
+
|
95
|
+
assert resp.success?
|
96
|
+
assert resp.data.include?(:transaction_id)
|
97
|
+
end
|
98
|
+
|
99
|
+
test 'credit void' do
|
100
|
+
resp = IPay::CC::Credit.refund(
|
101
|
+
:amount => '4.99',
|
102
|
+
:account_number => '4000009999999991',
|
103
|
+
:cvv => 123,
|
104
|
+
:expiration => CC_EXP,
|
105
|
+
:first_name => 'nick',
|
106
|
+
:last_name => 'wilson',
|
107
|
+
:address => '123 fake st',
|
108
|
+
:city => 'sometown',
|
109
|
+
:state => 'NY',
|
110
|
+
:postal_code => '90210',
|
111
|
+
:country => IPay::Countries::USA
|
112
|
+
)
|
113
|
+
|
114
|
+
assert resp.success?
|
115
|
+
assert resp.data.include?(:transaction_id)
|
116
|
+
|
117
|
+
sleep(3)
|
118
|
+
|
119
|
+
resp = IPay::CC::Credit.void(
|
120
|
+
:transaction_id => resp.data[:transaction_id]
|
121
|
+
)
|
122
|
+
|
123
|
+
assert resp.success?
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
data/test/test_errors.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestErrors < Test::Unit::TestCase
|
4
|
+
CC_EXP = "#{Date.today.month.to_s.rjust(2,'0')}#{Date.today.year.to_s[2..-1]}"
|
5
|
+
|
6
|
+
test 'sale declined' do
|
7
|
+
|
8
|
+
resp = IPay::CC::Debit.sale(
|
9
|
+
:amount => '1.05',
|
10
|
+
:account_number => '4000009999999991',
|
11
|
+
:cvv => 123,
|
12
|
+
:expiration => CC_EXP,
|
13
|
+
:first_name => 'nick',
|
14
|
+
:last_name => 'wilson',
|
15
|
+
:address => '123 fake st',
|
16
|
+
:city => 'sometown',
|
17
|
+
:state => 'NY',
|
18
|
+
:postal_code => '90210',
|
19
|
+
:country => IPay::Countries::USA
|
20
|
+
)
|
21
|
+
|
22
|
+
assert resp.error?
|
23
|
+
assert resp.status[:arc] == '05'
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'request timeout' do
|
28
|
+
|
29
|
+
assert_raise IPay::RequestTimeout do
|
30
|
+
resp = IPay::CC::Debit.sale(
|
31
|
+
:amount => '1.57',
|
32
|
+
:account_number => '4000009999999991',
|
33
|
+
:cvv => 123,
|
34
|
+
:expiration => CC_EXP,
|
35
|
+
:first_name => 'nick',
|
36
|
+
:last_name => 'wilson',
|
37
|
+
:address => '123 fake st',
|
38
|
+
:city => 'sometown',
|
39
|
+
:state => 'NY',
|
40
|
+
:postal_code => '90210',
|
41
|
+
:country => IPay::Countries::USA
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/test/test_wallet.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestWallet < Test::Unit::TestCase
|
4
|
+
CC_EXP = "#{Date.today.month.to_s.rjust(2,'0')}#{Date.today.year.to_s[2..-1]}"
|
5
|
+
|
6
|
+
test 'client insert' do
|
7
|
+
resp = IPay::Wallet::Client.insert(
|
8
|
+
:account => 'CC',
|
9
|
+
:billing_transaction => 2,
|
10
|
+
:account_number => '4000009999999991',
|
11
|
+
:cvv => 123,
|
12
|
+
:expiration => CC_EXP,
|
13
|
+
:first_name => 'nick',
|
14
|
+
:last_name => 'wilson',
|
15
|
+
:address => '123 fake st',
|
16
|
+
:city => 'sometown',
|
17
|
+
:state => 'NY',
|
18
|
+
:postal_code => '90210',
|
19
|
+
:country => IPay::Countries::USA
|
20
|
+
)
|
21
|
+
|
22
|
+
assert resp.success?
|
23
|
+
assert resp.data.include?(:transaction_id)
|
24
|
+
assert resp.data.include?(:client_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'client modify' do
|
28
|
+
|
29
|
+
resp = IPay::Wallet::Client.insert(
|
30
|
+
:account => 'CC',
|
31
|
+
:billing_transaction => 2,
|
32
|
+
:account_number => '4000009999999991',
|
33
|
+
:cvv => 123,
|
34
|
+
:expiration => CC_EXP,
|
35
|
+
:first_name => 'nick',
|
36
|
+
:last_name => 'wilson',
|
37
|
+
:address => '123 fake st',
|
38
|
+
:city => 'sometown',
|
39
|
+
:state => 'NY',
|
40
|
+
:postal_code => '90210',
|
41
|
+
:country => IPay::Countries::USA
|
42
|
+
)
|
43
|
+
|
44
|
+
assert resp.success?
|
45
|
+
assert resp.data.include?(:transaction_id)
|
46
|
+
assert resp.data.include?(:client_id)
|
47
|
+
|
48
|
+
sleep(3)
|
49
|
+
|
50
|
+
resp = IPay::Wallet::Client.modify(
|
51
|
+
:client_id => resp.data[:client_id],
|
52
|
+
:first_name => 'gregory washington'
|
53
|
+
)
|
54
|
+
|
55
|
+
assert resp.success?
|
56
|
+
assert resp.data.include?(:transaction_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
test 'client delete' do
|
60
|
+
resp = IPay::Wallet::Client.insert(
|
61
|
+
:account => 'CC',
|
62
|
+
:billing_transaction => 2,
|
63
|
+
:account_number => '4000009999999991',
|
64
|
+
:cvv => 123,
|
65
|
+
:expiration => CC_EXP,
|
66
|
+
:first_name => 'nick',
|
67
|
+
:last_name => 'wilson',
|
68
|
+
:address => '123 fake st',
|
69
|
+
:city => 'sometown',
|
70
|
+
:state => 'NY',
|
71
|
+
:postal_code => '90210',
|
72
|
+
:country => IPay::Countries::USA
|
73
|
+
)
|
74
|
+
|
75
|
+
assert resp.success?
|
76
|
+
assert resp.data.include?(:transaction_id)
|
77
|
+
assert resp.data.include?(:client_id)
|
78
|
+
|
79
|
+
sleep(3)
|
80
|
+
|
81
|
+
resp = IPay::Wallet::Client.delete(
|
82
|
+
:client_id => resp.data[:client_id]
|
83
|
+
)
|
84
|
+
|
85
|
+
assert resp.success?
|
86
|
+
assert resp.data.include?(:transaction_id)
|
87
|
+
assert resp.data.include?(:client_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
test 'account insert' do
|
91
|
+
resp = IPay::Wallet::Client.insert(
|
92
|
+
:account => 'CC',
|
93
|
+
:billing_transaction => 2,
|
94
|
+
:account_number => '4000009999999991',
|
95
|
+
:cvv => 123,
|
96
|
+
:expiration => CC_EXP,
|
97
|
+
:first_name => 'nick',
|
98
|
+
:last_name => 'wilson',
|
99
|
+
:address => '123 fake st',
|
100
|
+
:city => 'sometown',
|
101
|
+
:state => 'NY',
|
102
|
+
:postal_code => '90210',
|
103
|
+
:country => IPay::Countries::USA
|
104
|
+
)
|
105
|
+
|
106
|
+
assert resp.success?
|
107
|
+
assert resp.data.include?(:transaction_id)
|
108
|
+
assert resp.data.include?(:client_id)
|
109
|
+
|
110
|
+
sleep(3)
|
111
|
+
|
112
|
+
resp = IPay::Wallet::Account.insert(
|
113
|
+
:account => 'CC',
|
114
|
+
:billing_transaction => 2,
|
115
|
+
:account_number => '4000009999999991',
|
116
|
+
:cvv => 123,
|
117
|
+
:expiration => '0512',
|
118
|
+
:client_id => resp.data[:client_id]
|
119
|
+
)
|
120
|
+
assert resp.success?
|
121
|
+
assert resp.data.include?(:transaction_id)
|
122
|
+
end
|
123
|
+
|
124
|
+
test 'account modify' do
|
125
|
+
resp = IPay::Wallet::Client.insert(
|
126
|
+
:account => 'CC',
|
127
|
+
:billing_transaction => 2,
|
128
|
+
:account_number => '4000009999999991',
|
129
|
+
:cvv => 123,
|
130
|
+
:expiration => CC_EXP,
|
131
|
+
:first_name => 'nick',
|
132
|
+
:last_name => 'wilson',
|
133
|
+
:address => '123 fake st',
|
134
|
+
:city => 'sometown',
|
135
|
+
:state => 'NY',
|
136
|
+
:postal_code => '90210',
|
137
|
+
:country => IPay::Countries::USA
|
138
|
+
)
|
139
|
+
|
140
|
+
assert resp.success?
|
141
|
+
assert resp.data.include?(:transaction_id)
|
142
|
+
assert resp.data.include?(:client_id)
|
143
|
+
|
144
|
+
sleep(3)
|
145
|
+
|
146
|
+
resp = IPay::Wallet::Account.modify(
|
147
|
+
:account_id => resp.data[:account_id],
|
148
|
+
:billing_transaction => 2,
|
149
|
+
:account_number => '5100009999999997',
|
150
|
+
:cvv => 456,
|
151
|
+
:expiration => CC_EXP
|
152
|
+
)
|
153
|
+
|
154
|
+
assert resp.success?
|
155
|
+
assert resp.data.include?(:transaction_id)
|
156
|
+
end
|
157
|
+
|
158
|
+
test 'account delete' do
|
159
|
+
resp = IPay::Wallet::Client.insert(
|
160
|
+
:account => 'CC',
|
161
|
+
:billing_transaction => 2,
|
162
|
+
:account_number => '4000009999999991',
|
163
|
+
:cvv => 123,
|
164
|
+
:expiration => CC_EXP,
|
165
|
+
:first_name => 'nick',
|
166
|
+
:last_name => 'wilson',
|
167
|
+
:address => '123 fake st',
|
168
|
+
:city => 'sometown',
|
169
|
+
:state => 'NY',
|
170
|
+
:postal_code => '90210',
|
171
|
+
:country => IPay::Countries::USA
|
172
|
+
)
|
173
|
+
|
174
|
+
assert resp.success?
|
175
|
+
assert resp.data.include?(:transaction_id)
|
176
|
+
assert resp.data.include?(:client_id)
|
177
|
+
|
178
|
+
sleep(3)
|
179
|
+
|
180
|
+
resp = IPay::Wallet::Account.insert(
|
181
|
+
:account => 'CC',
|
182
|
+
:billing_transaction => 2,
|
183
|
+
:account_number => '4000009999999991',
|
184
|
+
:cvv => 123,
|
185
|
+
:expiration => '0512',
|
186
|
+
:client_id => resp.data[:client_id]
|
187
|
+
)
|
188
|
+
assert resp.success?
|
189
|
+
assert resp.data.include?(:transaction_id)
|
190
|
+
|
191
|
+
sleep(3)
|
192
|
+
|
193
|
+
resp = IPay::Wallet::Account.delete(
|
194
|
+
:account_id => resp.data[:account_id]
|
195
|
+
)
|
196
|
+
assert resp.success?
|
197
|
+
assert resp.data.include?(:transaction_id)
|
198
|
+
end
|
199
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ipay
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nick Wilson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-16 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: libxml-ruby
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description: iPay is a simple library for interfacing with Planet Payments payment solutions API, iPay. For more information visit http://www.ipay.com
|
28
|
+
email: wilson.nick@gmail.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- lib/console.rb
|
37
|
+
- lib/ipay/api_request.rb
|
38
|
+
- lib/ipay/cc.rb
|
39
|
+
- lib/ipay/certification.rb
|
40
|
+
- lib/ipay/config.rb
|
41
|
+
- lib/ipay/countries.rb
|
42
|
+
- lib/ipay/log.rb
|
43
|
+
- lib/ipay/network.rb
|
44
|
+
- lib/ipay/response.rb
|
45
|
+
- lib/ipay/util.rb
|
46
|
+
- lib/ipay/version.rb
|
47
|
+
- lib/ipay/wallet.rb
|
48
|
+
- lib/ipay/xml_request.rb
|
49
|
+
- lib/ipay.rb
|
50
|
+
- test/helper.rb
|
51
|
+
- test/test_cc.rb
|
52
|
+
- test/test_errors.rb
|
53
|
+
- test/test_wallet.rb
|
54
|
+
- example/certification.rb
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: https://github.com/AudioAddict/ipay
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.6.2
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: library for interfacing with iPay xml API
|
85
|
+
test_files: []
|
86
|
+
|