maxmind 0.1.2 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,6 @@
3
3
  coverage
4
4
  rdoc
5
5
  pkg
6
+ Gemfile.lock
7
+ .rvmrc
8
+ *.sublime*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'rspec', :version => 2, :cli => '--color --format doc' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('lib/eventricle.rb') { "spec/eventricle.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
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 'rubygems'
2
- require 'rake'
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "maxmind"
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
- begin
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
- if File.exist?('VERSION.yml')
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
- :requested_type => 'premium',
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
- request = Maxmind::Request.new('LICENSE_KEY', required_fields.merge(recommended_fields).merge(optional_fields))
38
- response = Maxmind::Response.new(request.query)
39
- pp response
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 'rubygems'
2
- gem 'httparty'
3
- require 'httparty'
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')
@@ -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, :license_key
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, :requested_type,
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
- def initialize(license_key, options = {})
13
- @license_key = license_key
14
-
15
- options.each do |k, v|
16
- self.instance_variable_set("@#{k}", v)
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
- def query(string = false)
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 => @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 => @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
- query = required_fields.merge(optional_fields)
55
- if string == false
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
- def get(query)
63
- response = HTTParty.get('http://minfraud3.maxmind.com/app/ccv2r', :query => query)
64
- return response.body
65
- end
66
-
67
- def email=(email)
68
- @email = Digest::MD5.hexdigest(email.downcase)
69
- end
70
-
71
- def username=(username)
72
- @username = Digest::MD5.hexdigest(username.downcase)
73
- end
74
-
75
- def password=(password)
76
- @password = Digest::MD5.hexdigest(password.downcase)
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, 'license key required' if @license_key.nil?
82
- raise ArgumentError, 'IP address required' if @client_ip.nil?
83
- raise ArgumentError, 'city required' if @city.nil?
84
- raise ArgumentError, 'region required' if @region.nil?
85
- raise ArgumentError, 'postal code required' if @postal.nil?
86
- raise ArgumentError, 'country required' if @country.nil?
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