kount 0.0.1

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjI5YTNkMmZiZTcxYWVlMmU1MzUyZWViMjc0ZmIzN2Y0ZDc2ZWExOA==
5
+ data.tar.gz: !binary |-
6
+ NmFhNWQ2MzEzYzYyMzU5ZmJkZTRjN2ZmOGFiZTRiMTZjYjlmNjBkNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTYwZTgxMmE2NjVlMjkxM2NmMWFmZTg2ZTk2YWRiYTYwYjA4OTA1MTlmMDdl
10
+ ZGZjOWM4MzUzYzRlMTI3YzcxOWZlNzg5Y2ZjZDc0NjhhMzQ2YTdjYmJlYjAz
11
+ Mzk2N2E4YWE2YmYxNjg5ZDkzM2VhOGM3M2FiMzlmNTg1NzI1N2E=
12
+ data.tar.gz: !binary |-
13
+ ODRkZjYzNThmM2UyZTNkN2IwMzY0ZTRkN2YyMWFlZTE0OWM0ODAzNWE4Y2Zh
14
+ MDg2NjlkMDEzYjg0YjNiOTU1NjYxZGQ2ODQ4ZjRmMmYxODViZWY5YTU1OGQ2
15
+ OGQ1NzBlNzFlMzIyNDEzMTI5ZThlZmFlZGQxM2Q0MTM5MGZhMTc=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Don Pflaster
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,122 @@
1
+ # Kount
2
+
3
+ Ruby interface for the Kount fraud detection service
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'kount'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install kount
18
+
19
+ ## Usage
20
+
21
+ ### Setup
22
+
23
+ Set up your credentials that you configured within the Kount administrative interface.
24
+ If you have a login for Kount, the folks at Kount should also have supplied you with a guide for generating your API and Risk Inquiry service certificates. The ultimate result is a certificate and key file in .pem format. There should be one of these each for the API and Risk Inquiry services, and for the test and production environments for a total of 8 files.
25
+
26
+ Suggested set up for a Rails app (place in an initializer):
27
+
28
+ # Set custom variables within gem
29
+
30
+ if Rails.env == 'production'
31
+ # API certificate
32
+ Kount.api_cert = 'config/keys/awc.kount.net.cert.pem'
33
+ Kount.api_cert_key = 'config/keys/awc.kount.net.key.pem'
34
+
35
+ # Risk Inquiry System Certificate
36
+ Kount.ris_cert = 'config/keys/ris.kount.net.cert.pem'
37
+ Kount.ris_cert_key = 'config/keys/ris.kount.net.key.pem'
38
+
39
+ Kount.mode = :production
40
+ else
41
+ # API certificate
42
+ Kount.api_cert = 'config/keys/awc.test.kount.net.cert.pem'
43
+ Kount.api_cert_key = 'config/keys/awc.test.kount.net.key.pem'
44
+
45
+ # Risk Inquiry System Certificate
46
+ Kount.ris_cert = 'config/keys/ris.test.kount.net.cert.pem'
47
+ Kount.ris_cert_key = 'config/keys/ris.test.kount.net.key.pem'
48
+
49
+ Kount.mode = :test
50
+ end
51
+
52
+ Also set up your merchant ID (this should also have been supplied to you by Kount):
53
+
54
+ Kount.merchant_id = '123456'
55
+
56
+ ### Making calls
57
+
58
+ In the following example, I am updating the status of an order to Declined through an API call to the Orders status endpoint.
59
+ The basic usage is to first create a payload hash:
60
+
61
+ payload = {
62
+ ['status[',kount_transaction_id,']'].join => 'D',
63
+ ['note[',kount_transaction_id,']'].join => text
64
+ }
65
+
66
+ Then instantiate a Kount::Request object and call "transmit" on it. Arguments:
67
+
68
+ 1: The symbol :api to indicate it is an API call, as opposed to the Risk Inquiry service which would be :risk
69
+ 2: The endpoint to call
70
+ 3: 'get' or 'post'
71
+ 4: The payload hash constructed in the previous step
72
+
73
+ ou = Kount::Request.new
74
+ ou.transmit(:api,'v1/orders/status.json','post',payload)
75
+
76
+ ### Risk Inquiry Service
77
+
78
+ Since the Risk Inquiry service has a very specific set of inputs, the Kount::RiskInquiry class is a layer of abstraction which accepts all of the input fields described in the Kount docs as accessor methods.
79
+
80
+ Instantiate the object:
81
+
82
+ ri = Kount::RiskInquiry.new
83
+
84
+ Set all the necessary fields to make a successful call:
85
+
86
+ ri.mode = 'Q' # Standard mode for initial post
87
+ ri.mack = 'Y'
88
+
89
+ ri.ipad = created_by_ip_address
90
+ ri.uniq = buyer.id
91
+ ri.name = buyer.name
92
+ # et cetera
93
+
94
+ Array fields accept arrays. You can set the array directly or iterate over another array you have prepared:
95
+
96
+ ri.prod_type = prods.map(&:name)
97
+
98
+ # or
99
+
100
+ ri.prod_type = []
101
+ prods.each { |prod|
102
+ ri.prod_type << prod.name
103
+ }
104
+
105
+ Custom fields that you have configured within Kount can be passed as a hash:
106
+
107
+ ri.custom_fields = {
108
+ :customfield1 => 'value1',
109
+ :customfield2 => 'value2'
110
+ }
111
+
112
+ Transmit the inquiry, which will return a Kount::Response object:
113
+
114
+ ri.request
115
+
116
+ ## Contributing
117
+
118
+ 1. Fork it
119
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin my-new-feature`)
122
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kount/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kount"
8
+ spec.version = Kount::VERSION
9
+ spec.authors = ["Don Pflaster"]
10
+ spec.email = ["don@ticketevolution.com"]
11
+ spec.description = %q{Interfaces with Kount Endpoints}
12
+ spec.summary = %q{}
13
+ spec.homepage = ""
14
+
15
+ spec.add_dependency 'curb'
16
+ spec.add_dependency 'json'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", ">= 2.0.0"
26
+ end
@@ -0,0 +1,23 @@
1
+ require "kount/version"
2
+ require 'kount/request'
3
+ require 'kount/response'
4
+ require 'kount/error'
5
+ require 'kount/defaults'
6
+
7
+ require 'curb'
8
+ require 'json'
9
+
10
+ module Kount
11
+ class << self
12
+ attr_accessor :api_cert
13
+ attr_accessor :api_cert_key
14
+ attr_accessor :ris_cert
15
+ attr_accessor :ris_cert_key
16
+ attr_accessor :mode
17
+ attr_accessor :merchant_id
18
+ # Your code goes here...
19
+ end
20
+ end
21
+
22
+ require 'kount/risk_inquiry'
23
+
@@ -0,0 +1,12 @@
1
+ module Kount
2
+ KOUNT_SERVER_URLS = {
3
+ :api => {
4
+ :test => 'https://api.test.kount.net/rpc/',
5
+ :production => 'https://api.kount.net/rpc/'
6
+ },
7
+ :risk => {
8
+ :test => 'https://risk.test.kount.net',
9
+ :production => 'https://risk.kount.net'
10
+ }
11
+ }
12
+ end
@@ -0,0 +1,11 @@
1
+ module Kount
2
+ class Error
3
+ attr_accessor :error_object
4
+
5
+ def initialize(error = nil)
6
+ @error_object = error
7
+ end
8
+ end
9
+ end
10
+
11
+
@@ -0,0 +1,45 @@
1
+ module Kount
2
+ class Request
3
+ def transmit(service_type,endpoint_url,get_or_post,query_params={})
4
+ url = [Kount::KOUNT_SERVER_URLS[service_type][Kount.mode],endpoint_url].join
5
+
6
+ Rails.logger.info ["Kount request: ",url].join if defined?(Rails)
7
+
8
+ c = Curl::Easy.new(url) do |curl|
9
+ curl.certtype = 'PEM'
10
+ if service_type == :api
11
+ curl.cert = Kount.api_cert
12
+ curl.cert_key = Kount.api_cert_key
13
+ elsif service_type == :risk
14
+ curl.cert = Kount.ris_cert
15
+ curl.cert_key = Kount.ris_cert_key
16
+ end
17
+ curl.connect_timeout = 5
18
+ end
19
+
20
+ if get_or_post.downcase == 'get'
21
+ c.http_get
22
+ elsif get_or_post.downcase == 'post'
23
+ c.http_post(get_post_fields(query_params))
24
+ end
25
+ Kount::Response.new(service_type,c)
26
+ rescue Exception => e
27
+ Kount::Error.new(e)
28
+ end
29
+
30
+ def get_post_fields(query_params)
31
+ fields = []
32
+ query_params.each { |k,v|
33
+ if v.is_a?(Array)
34
+ v.each { |element|
35
+ fields << Curl::PostField.content(k,element)
36
+ }
37
+ else
38
+ fields << Curl::PostField.content(k,v)
39
+ end
40
+ }
41
+ fields
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,40 @@
1
+ module Kount
2
+ class Response
3
+ attr_accessor :attributes
4
+ attr_reader :body
5
+
6
+ def initialize(service_type,response = nil)
7
+ raise ArgumentError, 'Missing response object' unless response
8
+ begin
9
+ if service_type == :api
10
+ @body = JSON.parse(response.body_str)
11
+ elsif service_type == :risk
12
+ @body = Hash[response.body_str.split("\n").map {|l| l.split("=") }]
13
+ end
14
+ rescue
15
+ raise ["Could not parse JSON response. Raw response: ",response.body_str].join("\n")
16
+ end
17
+ end
18
+
19
+ def result
20
+ @body['result']
21
+ end
22
+
23
+ def count
24
+ @body['count']
25
+ end
26
+
27
+ def errors
28
+ @body['errors']
29
+ end
30
+
31
+ def status
32
+ @body['status']
33
+ end
34
+
35
+ def is_risk_error?
36
+ @body['ERRO'].present?
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,92 @@
1
+ module Kount
2
+ class RiskInquiry < Request
3
+ MODES = ['Q','P','X','U']
4
+ REQUIRED_FIELDS = [
5
+ :anid, # Automatic Number Identification
6
+ :auth, # A or D, for accept or decline
7
+ :curr, # Country of currency submitted on order (USD)
8
+ :emal, # Customer's email
9
+ :ipad, # IP Address of Customer
10
+ :mack, # Merchant's acknowledgement to ship/process the order (Y)
11
+ :merc, # Merchant ID assigned to merchant by Kount
12
+ :mode, # Q, P, D, U
13
+ :ptok, # Payment token
14
+ :ptyp, # Payment type (CARD = Credit Card)
15
+ :sess, # Unique Session ID
16
+ :site, # Website Identifier of where order originated
17
+ :totl, # Total amount in currency (in pennies)
18
+ :tran, # Kount transaction ID (required for update modes U and X)
19
+ :vers # Version of Kount
20
+ ]
21
+
22
+ REQUIRED_ARRAY_FIELDS = [
23
+ :prod_type, # High level item descriptions
24
+ :prod_item, # Typically the SKU
25
+ :prod_desc, # Specific description
26
+ :prod_quant, # Quantity of item purchased
27
+ :prod_price # Price per unit item (in pennies)
28
+ ]
29
+ OPTIONAL_KEYS = [
30
+ :avst, # AVS Street (M, N, X)
31
+ :avsz, # AVS Zip (M, N, X)
32
+ :b2a1, # Billing address line 1
33
+ :b2a2, # Billing address Line 2
34
+ :b2cc, # Billing address Country
35
+ :b2ci, # Billing address City
36
+ :b2pc, # Billing Address Postal Code
37
+ :b2pn, # Bill-to Phone Number
38
+ :b2st, # Billing Address State/Province
39
+ :bpremise, #Bill-to premise for UK
40
+ :bstreet, # Bill-to street address for UK
41
+ :cash, # Total cash amount in currency submitted
42
+ :cvvr, # CVV response (M, N, X)
43
+ :dob, # Date of birth
44
+ :epoc, # Date customer account was created by merchant
45
+ :frmt, # Format of response (XML, JSON, YAML)
46
+ :gender, # M or F
47
+ :name, # Name submitted with order
48
+ :ordr, # Merchant's order number
49
+ :s2a1, # Shipping street address - Line 1
50
+ :s2a2, # Shipping street address - Line 2
51
+ :s2cc, # Shipping address - Country
52
+ :s2ci, # Shipping address - City
53
+ :s2em, # Shipping address - Email address
54
+ :s2nm, # Shipping address - Name
55
+ :s2pc, # Shipping address - Postal Code
56
+ :s2pn, # Shipping address - Ship-to Phone Number
57
+ :s2st, # Shipping address - State/Province
58
+ :shtp, # Shipping type
59
+ :spremise, # Ship-to premise for UK
60
+ :sstreet, # Ship-to Street for UK
61
+ :uniq, # Merchant assigned account number for consumer
62
+ :uagt # Consumer User-Agent HTTP Header
63
+ ]
64
+
65
+ (REQUIRED_FIELDS + REQUIRED_ARRAY_FIELDS + OPTIONAL_KEYS).each { |key| attr_accessor key }
66
+ attr_accessor :custom_fields # Hash
67
+
68
+ def initialize
69
+ @merc = Kount.merchant_id
70
+ @vers = Kount::API_VERSION
71
+ @custom_fields = {}
72
+ end
73
+
74
+ def payload
75
+ payload = {}
76
+ (REQUIRED_FIELDS + OPTIONAL_KEYS).each { |key|
77
+ payload[key.to_s.upcase] = self.send(key) unless self.send(key).nil?
78
+ }
79
+ (REQUIRED_ARRAY_FIELDS).each { |key|
80
+ payload[[key.to_s.upcase,'[]'].join] = self.send(key) unless self.send(key).nil?
81
+ }
82
+ custom_fields.each { |k,v|
83
+ payload.merge!({ ['UDF[',k.to_s.upcase,']'].join => v })
84
+ }
85
+ payload
86
+ end
87
+
88
+ def request
89
+ transmit(:risk,'','post',payload)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,4 @@
1
+ module Kount
2
+ VERSION = "0.0.1"
3
+ API_VERSION = '0555'
4
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kount::Request do
4
+ before do
5
+ Kount.cert = 'cert'
6
+ Kount.cert_key = 'cert_key'
7
+ @request = Kount::Request.new
8
+ end
9
+
10
+ it "requires a certificate and key" do
11
+ expect { Kount.cert }.to_not be_nil
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kount::Response do
4
+ before do
5
+ Kount.cert = 'cert'
6
+ Kount.cert_key = 'cert_key'
7
+
8
+ request = Kount::Request.new
9
+ @response = request.process!
10
+ end
11
+
12
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'kount'
5
+ require 'json'
6
+
7
+ RSpec.configure do |config|
8
+
9
+ end
10
+
11
+ def load_fixture(*filename)
12
+ #File.open(File.join('spec', 'data', *filename)).read
13
+ end
14
+
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kount
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Don Pflaster
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: curb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.0.0
83
+ description: Interfaces with Kount Endpoints
84
+ email:
85
+ - don@ticketevolution.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - kount.gemspec
96
+ - lib/kount.rb
97
+ - lib/kount/defaults.rb
98
+ - lib/kount/error.rb
99
+ - lib/kount/request.rb
100
+ - lib/kount/response.rb
101
+ - lib/kount/risk_inquiry.rb
102
+ - lib/kount/version.rb
103
+ - spec/kount/request_spec.rb
104
+ - spec/kount/response_spec.rb
105
+ - spec/spec_helper.rb
106
+ homepage: ''
107
+ licenses: []
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.1.11
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: ''
129
+ test_files:
130
+ - spec/kount/request_spec.rb
131
+ - spec/kount/response_spec.rb
132
+ - spec/spec_helper.rb
133
+ has_rdoc: