maxmind 0.1.2 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/Guardfile +6 -0
- data/README.md +134 -0
- data/Rakefile +9 -46
- data/examples/example.rb +6 -4
- data/lib/maxmind.rb +8 -3
- data/lib/maxmind/request.rb +117 -46
- data/lib/maxmind/response.rb +53 -47
- data/lib/maxmind/version.rb +3 -0
- data/maxmind.gemspec +19 -44
- data/spec/data/optional_fields.json +17 -0
- data/spec/data/recommended_fields.json +8 -0
- data/spec/data/required_fields.json +7 -0
- data/{test/fixtures → spec/data}/response.txt +0 -0
- data/spec/maxmind/request_spec.rb +50 -0
- data/spec/maxmind/response_spec.rb +72 -0
- data/spec/spec_helper.rb +30 -0
- metadata +120 -53
- data/.document +0 -5
- data/README.markdown +0 -93
- data/VERSION +0 -1
- data/test/maxmind_test.rb +0 -129
- data/test/test_helper.rb +0 -14
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
maxmind
|
2
|
+
==========
|
3
|
+
|
4
|
+
A wrapper around MaxMind's minFraud anti-fraud service.
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
In your Gemfile;
|
11
|
+
|
12
|
+
gem 'maxmind'
|
13
|
+
|
14
|
+
Tests
|
15
|
+
------------
|
16
|
+
|
17
|
+
bundle install
|
18
|
+
guard
|
19
|
+
|
20
|
+
Dependencies
|
21
|
+
------------
|
22
|
+
|
23
|
+
bundle install
|
24
|
+
|
25
|
+
Running Tests
|
26
|
+
-------------
|
27
|
+
|
28
|
+
Run `bundle install` to make sure you have all the dependencies. Once that's done, run:
|
29
|
+
|
30
|
+
rake test
|
31
|
+
|
32
|
+
Usage
|
33
|
+
-----
|
34
|
+
|
35
|
+
### Minimum Required ###
|
36
|
+
These are the only required fields to acquire a response from MaxMind.
|
37
|
+
|
38
|
+
require 'maxmind'
|
39
|
+
Maxmind.license_key = 'LICENSE_KEY'
|
40
|
+
request = Maxmind::Request.new(
|
41
|
+
:client_ip => '24.24.24.24',
|
42
|
+
:city => 'New York',
|
43
|
+
:region => 'NY',
|
44
|
+
:postal => '11434',
|
45
|
+
:country => 'US'
|
46
|
+
)
|
47
|
+
|
48
|
+
response = request.process!
|
49
|
+
|
50
|
+
|
51
|
+
### Recommended ###
|
52
|
+
For increased accuracy, these are the recommended fields to submit to MaxMind. The additional
|
53
|
+
fields here are optional and can be all or none.
|
54
|
+
|
55
|
+
require 'maxmind'
|
56
|
+
Maxmind.license_key = 'LICENSE_KEY'
|
57
|
+
request = Maxmind::Request.new(
|
58
|
+
:client_ip => '24.24.24.24',
|
59
|
+
:city => 'New York',
|
60
|
+
:region => 'NY',
|
61
|
+
:postal => '11434',
|
62
|
+
:country => 'US',
|
63
|
+
:domain => 'yahoo.com',
|
64
|
+
:bin => '549099',
|
65
|
+
:forwarded_ip => '24.24.24.25',
|
66
|
+
:email => 'test@test.com',
|
67
|
+
:username => 'test_carder_username',
|
68
|
+
:password => 'test_carder_password'
|
69
|
+
)
|
70
|
+
|
71
|
+
response = request.process!
|
72
|
+
|
73
|
+
### Thorough ###
|
74
|
+
This is every field available.
|
75
|
+
|
76
|
+
require 'maxmind'
|
77
|
+
Maxmind.license_key = 'LICENSE_KEY'
|
78
|
+
request = Maxmind::Request.new(
|
79
|
+
:client_ip => '24.24.24.24',
|
80
|
+
:city => 'New York',
|
81
|
+
:region => 'NY',
|
82
|
+
:postal => '11434',
|
83
|
+
:country => 'US',
|
84
|
+
:domain => 'yahoo.com',
|
85
|
+
:bin => '549099',
|
86
|
+
:forwarded_ip => '24.24.24.25',
|
87
|
+
:email => 'test@test.com',
|
88
|
+
:username => 'test_carder_username',
|
89
|
+
:password => 'test_carder_password'
|
90
|
+
:bin_name => 'MBNA America Bank',
|
91
|
+
:bin_phone => '800-421-2110',
|
92
|
+
:cust_phone => '212-242',
|
93
|
+
:request_type => 'premium',
|
94
|
+
:shipping_address => '145-50 157th Street',
|
95
|
+
:shipping_city => 'Jamaica',
|
96
|
+
:shipping_region => 'NY',
|
97
|
+
:shipping_postal => '11434',
|
98
|
+
:shipping_country => 'US',
|
99
|
+
:transaction_id => '1234',
|
100
|
+
:session_id => 'abcd9876',
|
101
|
+
:user_agent => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1',
|
102
|
+
:accept_language => 'en-us'
|
103
|
+
)
|
104
|
+
|
105
|
+
response = request.process!
|
106
|
+
|
107
|
+
|
108
|
+
Also see examples/example.rb
|
109
|
+
|
110
|
+
TODO
|
111
|
+
----
|
112
|
+
* Improve specs (eg, test server failover)
|
113
|
+
|
114
|
+
Reference
|
115
|
+
---------
|
116
|
+
[minFraud API Reference](http://www.maxmind.com/app/ccv)
|
117
|
+
|
118
|
+
Contributors
|
119
|
+
------------
|
120
|
+
Sam Oliver <sam@samoliver.com>
|
121
|
+
Nick Wilson <nick@di.fm>
|
122
|
+
Wolfram Arnold <wolfram@rubyfocus.biz>
|
123
|
+
Jonathan Lim <snowblink@gmail.com>
|
124
|
+
Tom Blomfield
|
125
|
+
Thomas Morgan
|
126
|
+
Tinu Cleatus <tinu.cleatus@me.com>
|
127
|
+
|
128
|
+
Thanks to all :)
|
129
|
+
|
130
|
+
Copyright
|
131
|
+
---------
|
132
|
+
Copyright (c) 2009 Adam Daniels <adam@mediadrive.ca>.
|
133
|
+
|
134
|
+
See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,57 +1,20 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
gem.summary = %Q{TODO}
|
9
|
-
gem.email = "adam@mediadrive.ca"
|
10
|
-
gem.homepage = "http://github.com/adam12/maxmind"
|
11
|
-
gem.authors = ["Adam Daniels"]
|
12
|
-
gem.add_dependency 'httparty'
|
13
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
-
end
|
15
|
-
|
16
|
-
rescue LoadError
|
17
|
-
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
-
end
|
19
|
-
|
20
|
-
require 'rake/testtask'
|
21
|
-
Rake::TestTask.new(:test) do |test|
|
22
|
-
test.libs << 'lib' << 'test'
|
23
|
-
test.pattern = 'test/**/*_test.rb'
|
24
|
-
test.verbose = true
|
4
|
+
desc "Run the test suite"
|
5
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
6
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
7
|
+
t.rspec_opts = %w(--color --format doc)
|
25
8
|
end
|
26
9
|
|
27
|
-
|
28
|
-
require 'rcov/rcovtask'
|
29
|
-
Rcov::RcovTask.new do |test|
|
30
|
-
test.libs << 'test'
|
31
|
-
test.pattern = 'test/**/*_test.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
34
|
-
rescue LoadError
|
35
|
-
task :rcov do
|
36
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
-
end
|
38
|
-
end
|
10
|
+
task :default => :spec
|
39
11
|
|
40
|
-
|
41
|
-
task :default => :test
|
42
|
-
|
43
|
-
require 'rake/rdoctask'
|
12
|
+
require 'rdoc/task'
|
44
13
|
Rake::RDocTask.new do |rdoc|
|
45
|
-
|
46
|
-
config = YAML.load(File.read('VERSION.yml'))
|
47
|
-
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
48
|
-
else
|
49
|
-
version = ""
|
50
|
-
end
|
14
|
+
version = File.read 'VERSION' rescue nil
|
51
15
|
|
52
16
|
rdoc.rdoc_dir = 'rdoc'
|
53
17
|
rdoc.title = "maxmind #{version}"
|
54
18
|
rdoc.rdoc_files.include('README*')
|
55
19
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
20
|
end
|
57
|
-
|
data/examples/example.rb
CHANGED
@@ -22,7 +22,7 @@ optional_fields = {
|
|
22
22
|
:bin_name => 'MBNA America Bank',
|
23
23
|
:bin_phone => '800-421-2110',
|
24
24
|
:cust_phone => '212-242',
|
25
|
-
:
|
25
|
+
:request_type => 'premium',
|
26
26
|
:shipping_address => '145-50 157th Street',
|
27
27
|
:shipping_city => 'Jamaica',
|
28
28
|
:shipping_region => 'NY',
|
@@ -34,6 +34,8 @@ optional_fields = {
|
|
34
34
|
:accept_language => 'en-us'
|
35
35
|
}
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
Maxmind.license_key = 'LICENSE_KEY'
|
38
|
+
Maxmind::Request.default_request_type = 'standard'
|
39
|
+
request = Maxmind::Request.new(required_fields.merge(recommended_fields).merge(optional_fields))
|
40
|
+
response = request.process!
|
41
|
+
pp response
|
data/lib/maxmind.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
require '
|
1
|
+
require 'active_support'
|
2
|
+
unless Module.respond_to?(:mattr_accessor)
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'# rescue nil # this may be needed for ActiveSupport versions >= 3.x
|
4
|
+
end
|
5
|
+
require 'active_support/core_ext/hash'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
require 'uri'
|
4
9
|
require 'digest/md5'
|
5
10
|
require File.join(File.dirname(__FILE__), 'maxmind/request')
|
6
11
|
require File.join(File.dirname(__FILE__), 'maxmind/response')
|
data/lib/maxmind/request.rb
CHANGED
@@ -1,41 +1,107 @@
|
|
1
1
|
module Maxmind
|
2
|
+
# Your license key
|
3
|
+
class << self
|
4
|
+
attr_accessor :license_key
|
5
|
+
end
|
6
|
+
|
7
|
+
SERVERS = %w(minfraud.maxmind.com minfraud-us-east.maxmind.com minfraud-us-west.maxmind.com)
|
8
|
+
|
2
9
|
class Request
|
10
|
+
DefaultTimeout = 60
|
11
|
+
|
12
|
+
# optionally set a default request type (one of 'standard' or 'premium')
|
13
|
+
# Maxmind's default behavior is to use premium if you have credits, else use standard
|
14
|
+
class << self
|
15
|
+
attr_accessor :default_request_type
|
16
|
+
attr_accessor :timeout
|
17
|
+
end
|
18
|
+
|
19
|
+
|
3
20
|
# Required Fields
|
4
|
-
attr_accessor :client_ip, :city, :region, :postal, :country
|
5
|
-
|
21
|
+
attr_accessor :client_ip, :city, :region, :postal, :country
|
22
|
+
|
6
23
|
# Optional Fields
|
7
|
-
attr_accessor :domain, :bin, :bin_name, :bin_phone, :cust_phone, :
|
24
|
+
attr_accessor :domain, :bin, :bin_name, :bin_phone, :cust_phone, :request_type,
|
8
25
|
:forwarded_ip, :email, :username, :password, :transaction_id, :session_id,
|
9
26
|
:shipping_address, :shipping_city, :shipping_region, :shipping_postal,
|
10
|
-
:shipping_country, :user_agent, :accept_language
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
27
|
+
:shipping_country, :user_agent, :accept_language, :order_amount,
|
28
|
+
:order_currency
|
29
|
+
|
30
|
+
def initialize(attrs={})
|
31
|
+
self.attributes = attrs
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def attributes=(attrs={})
|
36
|
+
attrs.each do |k, v|
|
37
|
+
self.send("#{k}=", v)
|
17
38
|
end
|
18
39
|
end
|
19
|
-
|
20
|
-
|
40
|
+
|
41
|
+
|
42
|
+
# email domain ... if a full email is provided, take just the domain portion
|
43
|
+
def domain=(email)
|
44
|
+
@domain = if email =~ /@(.+)/
|
45
|
+
$1
|
46
|
+
else
|
47
|
+
email
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# customer email ... sends just an MD5 hash of the email.
|
52
|
+
# also sets the email domain at the same time.
|
53
|
+
def email=(email)
|
54
|
+
@email = Digest::MD5.hexdigest(email.downcase)
|
55
|
+
self.domain = email unless domain
|
56
|
+
end
|
57
|
+
|
58
|
+
def username=(username)
|
59
|
+
@username = Digest::MD5.hexdigest(username.downcase)
|
60
|
+
end
|
61
|
+
|
62
|
+
def password=(password)
|
63
|
+
@password = Digest::MD5.hexdigest(password.downcase)
|
64
|
+
end
|
65
|
+
|
66
|
+
# if a full card number is passed, grab just the first 6 digits (which is the bank id number)
|
67
|
+
def bin=(bin)
|
68
|
+
@bin = bin ? bin[0,6] : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def process!
|
73
|
+
resp = post(query)
|
74
|
+
Maxmind::Response.new(resp)
|
75
|
+
end
|
76
|
+
|
77
|
+
def process
|
78
|
+
process!
|
79
|
+
rescue Exception => e
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def query
|
21
87
|
validate
|
22
|
-
|
88
|
+
|
23
89
|
required_fields = {
|
24
90
|
:i => @client_ip,
|
25
91
|
:city => @city,
|
26
92
|
:region => @region,
|
27
93
|
:postal => @postal,
|
28
94
|
:country => @country,
|
29
|
-
:license_key =>
|
95
|
+
:license_key => Maxmind::license_key
|
30
96
|
}
|
31
|
-
|
97
|
+
|
32
98
|
optional_fields = {
|
33
99
|
:domain => @domain,
|
34
100
|
:bin => @bin,
|
35
101
|
:binName => @bin_name,
|
36
102
|
:binPhone => @bin_phone,
|
37
103
|
:custPhone => @cust_phone,
|
38
|
-
:requested_type => @
|
104
|
+
:requested_type => @request_type || self.class.default_request_type,
|
39
105
|
:forwardedIP => @forwarded_ip,
|
40
106
|
:emailMD5 => @email,
|
41
107
|
:usernameMD5 => @username,
|
@@ -50,40 +116,45 @@ module Maxmind
|
|
50
116
|
:user_agent => @user_agent,
|
51
117
|
:accept_language => @accept_langage
|
52
118
|
}
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
return get(query.reject {|k, v| v.nil? })
|
57
|
-
else
|
58
|
-
return query.reject {|k, v| v.nil? }.to_params
|
59
|
-
end
|
119
|
+
|
120
|
+
field_set = required_fields.merge(optional_fields)
|
121
|
+
field_set.reject {|k, v| v.nil? }
|
60
122
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
123
|
+
|
124
|
+
# Upon a failure at the first URL, will automatically retry with the
|
125
|
+
# second & third ones before finally raising an exception
|
126
|
+
def post(query_params)
|
127
|
+
servers ||= SERVERS.map{|hostname| "https://#{hostname}/app/ccv2r"}
|
128
|
+
url = URI.parse(servers.shift)
|
129
|
+
|
130
|
+
req = Net::HTTP::Post.new(url.path)
|
131
|
+
req.set_form_data(query_params)
|
132
|
+
|
133
|
+
h = Net::HTTP.new(url.host, url.port)
|
134
|
+
h.use_ssl = true
|
135
|
+
h.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
136
|
+
|
137
|
+
# set some timeouts
|
138
|
+
h.open_timeout = 60 # this blocks forever by default, lets be a bit less crazy.
|
139
|
+
h.read_timeout = self.class.timeout || DefaultTimeout
|
140
|
+
h.ssl_timeout = self.class.timeout || DefaultTimeout
|
141
|
+
|
142
|
+
response = h.start { |http| http.request(req) }
|
143
|
+
response.body
|
144
|
+
|
145
|
+
rescue Exception => e
|
146
|
+
retry if servers.size > 0
|
147
|
+
raise e
|
77
148
|
end
|
78
149
|
|
79
150
|
protected
|
80
151
|
def validate
|
81
|
-
raise ArgumentError, '
|
82
|
-
raise ArgumentError, 'IP address required'
|
83
|
-
raise ArgumentError, '
|
84
|
-
raise ArgumentError, '
|
85
|
-
raise ArgumentError, '
|
86
|
-
raise ArgumentError, '
|
152
|
+
raise ArgumentError, 'License key is required' unless Maxmind::license_key
|
153
|
+
raise ArgumentError, 'IP address is required' unless client_ip
|
154
|
+
raise ArgumentError, 'City is required' unless city
|
155
|
+
raise ArgumentError, 'Region is required' unless region
|
156
|
+
raise ArgumentError, 'Postal code is required' unless postal
|
157
|
+
raise ArgumentError, 'Country is required' unless country
|
87
158
|
end
|
88
159
|
end
|
89
|
-
end
|
160
|
+
end
|