hamburglar 0.1.0
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/README.markdown +105 -0
- data/Rakefile +51 -0
- data/lib/cacert.pem +3910 -0
- data/lib/hamburglar/config.rb +14 -0
- data/lib/hamburglar/errors.rb +26 -0
- data/lib/hamburglar/gateways/base.rb +152 -0
- data/lib/hamburglar/gateways/max_mind.rb +68 -0
- data/lib/hamburglar/gateways.rb +6 -0
- data/lib/hamburglar/report.rb +61 -0
- data/lib/hamburglar/version.rb +3 -0
- data/lib/hamburglar.rb +19 -0
- metadata +109 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hamburglar
|
2
|
+
# Raised when trying to assign an invalid gateway to Hamburglar.gateway
|
3
|
+
class InvalidGateway < StandardError
|
4
|
+
def initialize(gateway = nil)
|
5
|
+
msg = "Invalid gateway"
|
6
|
+
msg << ", #{gateway}" if gateway
|
7
|
+
super msg
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Raised when trying to assign an invalid gateway URL
|
12
|
+
class InvalidURL < StandardError
|
13
|
+
def initialize(url = nil)
|
14
|
+
msg = "Invalid url"
|
15
|
+
msg << ", #{url}" if url
|
16
|
+
super msg
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Raised if Hamburglar::Gateways::Base.validate! fails
|
21
|
+
class InvalidRequest < StandardError
|
22
|
+
def initialize(msg = nil)
|
23
|
+
super "Invalid request"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Hamburglar
|
5
|
+
module Gateways
|
6
|
+
# Hamburglar::Gateways::Base is the main class that handles sending API
|
7
|
+
# requests to upstream providers. All other gateways should inherit from
|
8
|
+
# this class
|
9
|
+
class Base
|
10
|
+
|
11
|
+
URL_REGEX = /https?:\/\/[\S]+/
|
12
|
+
|
13
|
+
# The parameters for the API request
|
14
|
+
attr_reader :params
|
15
|
+
|
16
|
+
# Errors returned when validating or submitting a request
|
17
|
+
attr_reader :errors
|
18
|
+
|
19
|
+
# Response returned by an API call
|
20
|
+
attr_reader :response
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# The API URL
|
24
|
+
attr_reader :api_url
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(params = {})
|
28
|
+
defaults = (Hamburglar.config.credentials || {}).select do |key, val|
|
29
|
+
optional_params.include? key
|
30
|
+
end
|
31
|
+
@params = Hash[defaults].merge(params)
|
32
|
+
@errors = {}
|
33
|
+
@response = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get or set the API URL for the gateway
|
37
|
+
def self.set_api_url(url = '')
|
38
|
+
if url.match URL_REGEX
|
39
|
+
@api_url = url
|
40
|
+
else
|
41
|
+
raise Hamburglar::InvalidURL, url
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set required parameters for an API call
|
46
|
+
def self.set_required_params(*params)
|
47
|
+
@required_params = params
|
48
|
+
end
|
49
|
+
|
50
|
+
# Required parameters for an API call
|
51
|
+
def self.required_params
|
52
|
+
@required_params || []
|
53
|
+
end
|
54
|
+
|
55
|
+
# Validate presence of required_params
|
56
|
+
#
|
57
|
+
# Returns false if a parameter isn't set
|
58
|
+
def validate(revalidate = false)
|
59
|
+
@validated = false if revalidate
|
60
|
+
unless @validated
|
61
|
+
@errors[:missing_parameters] = []
|
62
|
+
self.class.required_params.each do |req|
|
63
|
+
unless @params.has_key?(req)
|
64
|
+
@errors[:missing_parameters] << req
|
65
|
+
end
|
66
|
+
end
|
67
|
+
@validated = true
|
68
|
+
end
|
69
|
+
@errors[:missing_parameters].empty?
|
70
|
+
end
|
71
|
+
alias_method :valid?, :validate
|
72
|
+
|
73
|
+
# Validate presence of required_params
|
74
|
+
#
|
75
|
+
# Raises Hamburglar::InvalidRequest if validation fails
|
76
|
+
def validate!
|
77
|
+
validate || raise(Hamburglar::InvalidRequest)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Submit a request upstream to generate a fraud report
|
81
|
+
def submit
|
82
|
+
return false unless valid?
|
83
|
+
url = "#{self.class.api_url}?#{query_string}"
|
84
|
+
if res = fetch(url)
|
85
|
+
@response = parse_response(res.body)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Optional parameters that *may* be present in a query
|
90
|
+
#
|
91
|
+
# This method should be overridden by classes than inherit from
|
92
|
+
# Hamburglar::Gateways::Base
|
93
|
+
#
|
94
|
+
# Defaults to self.required_params
|
95
|
+
def optional_params
|
96
|
+
self.class.required_params
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Formats @params into a query string for an HTTP GET request
|
102
|
+
def query_string
|
103
|
+
@params.map { |key, val| "#{key}=#{CGI.escape(val.to_s)}" }.join('&')
|
104
|
+
end
|
105
|
+
|
106
|
+
# Parses raw data returned from an API call
|
107
|
+
#
|
108
|
+
# This method should be overwritten by any API subclasses that
|
109
|
+
# return data in a different format
|
110
|
+
#
|
111
|
+
# Returns [Hash]
|
112
|
+
def parse_response(raw = '')
|
113
|
+
data = raw.to_s.split(';').map do |line|
|
114
|
+
key, val = line.split('=')
|
115
|
+
if key.to_s != "" && val.to_s != ""
|
116
|
+
[key.to_sym, val]
|
117
|
+
else
|
118
|
+
next
|
119
|
+
end
|
120
|
+
end
|
121
|
+
Hash[data]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Performs a GET request on the given URI, redirects if needed
|
125
|
+
#
|
126
|
+
# See Following Redirection at
|
127
|
+
# http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTP.html
|
128
|
+
def fetch(uri_str, limit = 10)
|
129
|
+
# You should choose better exception.
|
130
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
131
|
+
|
132
|
+
uri = URI.parse(uri_str)
|
133
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
134
|
+
if uri.scheme == 'https'
|
135
|
+
http.use_ssl = true
|
136
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
137
|
+
http.ca_file = File.expand_path('../../../cacert.pem', __FILE__)
|
138
|
+
end
|
139
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
140
|
+
response = http.start { |http| http.request(request) }
|
141
|
+
|
142
|
+
case response
|
143
|
+
when Net::HTTPSuccess then response
|
144
|
+
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
145
|
+
else
|
146
|
+
response.error!
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Hamburglar
|
2
|
+
module Gateways
|
3
|
+
# The MaxMind module contains classes for working
|
4
|
+
# with MaxMind's minFraud and Telephone Verification APIs
|
5
|
+
module MaxMind
|
6
|
+
# The MinFraud class handles fraud verification
|
7
|
+
# through MaxMind's minFraud API.
|
8
|
+
#
|
9
|
+
# See: http://www.maxmind.com/app/ccv
|
10
|
+
class MinFraud < Base
|
11
|
+
# The MaxMind API URL
|
12
|
+
set_api_url "https://minfraud2.maxmind.com/app/ccv2r"
|
13
|
+
|
14
|
+
# Required parameters for a minFraud API call
|
15
|
+
set_required_params :i, :city, :region, :postal, :country, :license_key
|
16
|
+
|
17
|
+
# Optional parameters
|
18
|
+
def optional_params
|
19
|
+
[
|
20
|
+
:i,
|
21
|
+
:city,
|
22
|
+
:region,
|
23
|
+
:postal,
|
24
|
+
:country,
|
25
|
+
:license_key,
|
26
|
+
:domain,
|
27
|
+
:bin,
|
28
|
+
:binName,
|
29
|
+
:binPhone,
|
30
|
+
:custPhone,
|
31
|
+
:requested_type,
|
32
|
+
:forwardedIP,
|
33
|
+
:emailMD5,
|
34
|
+
:usernameMD5,
|
35
|
+
:passwordMD5,
|
36
|
+
:shipAddr,
|
37
|
+
:shipCity,
|
38
|
+
:shipRegion,
|
39
|
+
:shipPostal,
|
40
|
+
:shipCountry,
|
41
|
+
:textID,
|
42
|
+
:sessionID,
|
43
|
+
:user_agent,
|
44
|
+
:accept_language
|
45
|
+
].freeze
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# The TelephoneVerification class handles fraud verification
|
50
|
+
# through MaxMind's Telephone Verification API
|
51
|
+
#
|
52
|
+
# See: http://www.maxmind.com/app/telephone_api
|
53
|
+
class TelephoneVerification < Base
|
54
|
+
# The MaxMind Telephone Verification API URL
|
55
|
+
set_api_url "https://www.maxmind.com/app/telephone_http"
|
56
|
+
|
57
|
+
# Required parameters for a Telephone Verification API call
|
58
|
+
set_required_params :l, :phone
|
59
|
+
|
60
|
+
# Optional parameters
|
61
|
+
def optional_params
|
62
|
+
[:l, :phone, :verify_code].freeze
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Hamburglar
|
2
|
+
# Hamburglar::Report is the main class for generating fraud reports
|
3
|
+
class Report
|
4
|
+
# Parameters that will be used to generate this fraud report
|
5
|
+
attr_reader :params
|
6
|
+
|
7
|
+
# Response from gateway
|
8
|
+
attr_reader :response
|
9
|
+
|
10
|
+
def initialize(params = {})
|
11
|
+
@gateway = params.delete(:gateway) || Hamburglar.config.gateway
|
12
|
+
@params = params
|
13
|
+
@response = generate_report!
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
if @response && @response[method.to_sym]
|
18
|
+
@response[method.to_sym]
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(key)
|
25
|
+
@response.has_key?(key) || super
|
26
|
+
end
|
27
|
+
|
28
|
+
def fraud?
|
29
|
+
@fraud = true if @response.nil? || @response.empty?
|
30
|
+
return @fraud if @fraud
|
31
|
+
if Hamburglar.config.fraud_proc.nil?
|
32
|
+
@fraud = @response[:score].to_f >= Hamburglar.config.fraud_score.to_f
|
33
|
+
else
|
34
|
+
@fraud = Hamburglar.config.fraud_proc.call(self) == true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def generate_report!
|
41
|
+
api = gateway.new(@params)
|
42
|
+
if api.valid?
|
43
|
+
api.submit
|
44
|
+
else
|
45
|
+
api.errors
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def gateway
|
50
|
+
case @gateway.to_s
|
51
|
+
when /min_fraud/
|
52
|
+
Gateways::MaxMind::MinFraud
|
53
|
+
when /telephone/
|
54
|
+
Gateways::MaxMind::TelephoneVerification
|
55
|
+
else
|
56
|
+
raise Hamburglar::InvalidGateway, @gateway
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/hamburglar.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'hamburglar/errors'
|
2
|
+
|
3
|
+
module Hamburglar
|
4
|
+
autoload :Version, 'hamburglar/version'
|
5
|
+
autoload :Config, 'hamburglar/config'
|
6
|
+
autoload :Report, 'hamburglar/report'
|
7
|
+
autoload :Gateways, 'hamburglar/gateways'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :config
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
yield config if block_given?
|
15
|
+
config
|
16
|
+
end
|
17
|
+
|
18
|
+
self.config = Config.new
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hamburglar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joshua Priddle
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-03 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "2.6"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.8.7
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: fakeweb
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.3.0
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: vcr
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "1.10"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
description: Hamburglar helps you prevent fraudulent orders
|
61
|
+
email: jpriddle@site5.com
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files:
|
67
|
+
- README.markdown
|
68
|
+
files:
|
69
|
+
- Rakefile
|
70
|
+
- README.markdown
|
71
|
+
- lib/cacert.pem
|
72
|
+
- lib/hamburglar/config.rb
|
73
|
+
- lib/hamburglar/errors.rb
|
74
|
+
- lib/hamburglar/gateways/base.rb
|
75
|
+
- lib/hamburglar/gateways/max_mind.rb
|
76
|
+
- lib/hamburglar/gateways.rb
|
77
|
+
- lib/hamburglar/report.rb
|
78
|
+
- lib/hamburglar/version.rb
|
79
|
+
- lib/hamburglar.rb
|
80
|
+
has_rdoc: true
|
81
|
+
homepage: https://github.com/site5/hamburglar
|
82
|
+
licenses: []
|
83
|
+
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options:
|
86
|
+
- --charset=UTF-8
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.6.2
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Hamburglar helps you prevent fraudulent orders
|
108
|
+
test_files: []
|
109
|
+
|