workarea-kount 3.3.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.
- checksums.yaml +7 -0
- data/.editorconfig +20 -0
- data/.eslintrc +24 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/ci.yml +58 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +212 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +17 -0
- data/LICENSE.md +3 -0
- data/README.md +98 -0
- data/Rakefile +53 -0
- data/app/controllers/workarea/admin/orders_controller.decorator +5 -0
- data/app/controllers/workarea/storefront/checkout/place_order_controller.decorator +11 -0
- data/app/controllers/workarea/storefront/kount_controller.rb +19 -0
- data/app/controllers/workarea/storefront/kount_orders_controller.rb +40 -0
- data/app/lib/active_shipping/rate_estimate.decorator +10 -0
- data/app/mailers/workarea/storefront/order_mailer.decorator +14 -0
- data/app/models/workarea/checkout.decorator +37 -0
- data/app/models/workarea/checkout/fraud/kount_analyzer.rb +71 -0
- data/app/models/workarea/kount/ens_event.rb +13 -0
- data/app/models/workarea/kount/order.rb +14 -0
- data/app/models/workarea/order.decorator +62 -0
- data/app/models/workarea/order/fraud_decision.decorator +9 -0
- data/app/models/workarea/order/item.decorator +8 -0
- data/app/models/workarea/order/status/review.rb +13 -0
- data/app/models/workarea/payment/credit_card.decorator +13 -0
- data/app/models/workarea/payment/payment.decorator +11 -0
- data/app/models/workarea/payment/status/kount_declined.rb +13 -0
- data/app/models/workarea/payment/status/kount_review.rb +13 -0
- data/app/models/workarea/payment/tender/credit_card.decorator +10 -0
- data/app/models/workarea/search/admin.decorator +12 -0
- data/app/models/workarea/search/admin/order.decorator +7 -0
- data/app/models/workarea/shipping/rate_lookup.decorator +26 -0
- data/app/models/workarea/shipping/service.decorator +9 -0
- data/app/models/workarea/shipping/service_selection.decorator +7 -0
- data/app/models/workarea/shipping_option.decorator +27 -0
- data/app/services/workarea/kount/cancel_order_under_review.rb +28 -0
- data/app/services/workarea/kount/event.rb +43 -0
- data/app/services/workarea/kount/event/dmc_event.rb +9 -0
- data/app/services/workarea/kount/event/risk_change_event.rb +21 -0
- data/app/services/workarea/kount/event/special_alert_event.rb +21 -0
- data/app/services/workarea/kount/event/workflow_event.rb +25 -0
- data/app/services/workarea/kount/event_batch.rb +37 -0
- data/app/services/workarea/kount/order_fraud_update_service.rb +42 -0
- data/app/services/workarea/kount/ris_inquiry.rb +120 -0
- data/app/services/workarea/kount/ris_inquiry/product.rb +53 -0
- data/app/services/workarea/kount/ris_request.rb +84 -0
- data/app/services/workarea/kount/ris_update.rb +39 -0
- data/app/view_models/workarea/admin/order_view_model.decorator +35 -0
- data/app/view_models/workarea/storefront/order_view_model.decorator +11 -0
- data/app/views/workarea/admin/orders/_kount.html.haml +28 -0
- data/app/views/workarea/admin/orders/kount.html.haml +46 -0
- data/app/views/workarea/storefront/checkouts/_kount_data_collector.html.haml +4 -0
- data/app/views/workarea/storefront/order_mailer/hold_denial.html.haml +14 -0
- data/app/workers/workarea/kount/process_review_order.rb +75 -0
- data/app/workers/workarea/kount/update_ris_inquiry.rb +31 -0
- data/bin/rails +18 -0
- data/config/initializers/appends.rb +11 -0
- data/config/initializers/workarea.rb +26 -0
- data/config/locales/en.yml +26 -0
- data/config/routes.rb +14 -0
- data/doc/Kount Technical Specification Guide 5.5.5.pdf +0 -0
- data/lib/workarea/kount.rb +87 -0
- data/lib/workarea/kount/address.rb +39 -0
- data/lib/workarea/kount/bogus_gateway.rb +415 -0
- data/lib/workarea/kount/engine.rb +8 -0
- data/lib/workarea/kount/errors.rb +2 -0
- data/lib/workarea/kount/gateway.rb +50 -0
- data/lib/workarea/kount/mappings.rb +195 -0
- data/lib/workarea/kount/payment_types.rb +50 -0
- data/lib/workarea/kount/response.rb +141 -0
- data/lib/workarea/kount/security_mash.rb +79 -0
- data/lib/workarea/kount/user_defined_fields.rb +16 -0
- data/lib/workarea/kount/version.rb +5 -0
- data/lib/workarea/mailer_previews/storefront/order_mailer_preview.rb +10 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +34 -0
- data/test/dummy/bin/update +29 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +9 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +56 -0
- data/test/dummy/config/environments/production.rb +86 -0
- data/test/dummy/config/environments/test.rb +44 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/puma.rb +47 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/factories/kount.rb +164 -0
- data/test/integration/workarea/api/admin/payment_transactions_integration_test.decorator +29 -0
- data/test/integration/workarea/kount_moneris_integration_test.rb +37 -0
- data/test/integration/workarea/storefront/kount_integration_test.rb +37 -0
- data/test/integration/workarea/storefront/kount_orders_integration_test.rb +89 -0
- data/test/lib/workarea/kount/response_test.rb +95 -0
- data/test/lib/workarea/kount/security_mash_test.rb +62 -0
- data/test/models/workarea/checkout/fraud/kount_analyzer_test.rb +44 -0
- data/test/models/workarea/checkout_test.decorator +22 -0
- data/test/models/workarea/order/queries_test.decorator +28 -0
- data/test/models/workarea/order_test.decorator +25 -0
- data/test/models/workarea/payment/payment_test.rb +106 -0
- data/test/models/workarea/payment/saved_credit_card_test.rb +24 -0
- data/test/models/workarea/payment_test.decorator +54 -0
- data/test/models/workarea/shipping_option_test.rb +29 -0
- data/test/services/workarea/kount/event_batch_test.rb +95 -0
- data/test/services/workarea/kount/event_test.rb +51 -0
- data/test/services/workarea/kount/order_fraud_update_service_test.rb +48 -0
- data/test/support/workarea/kount_api_config.rb +27 -0
- data/test/system/workarea/kount_system_test.rb +72 -0
- data/test/system/workarea/storefront/kount_guest_checkout_system_test.rb +32 -0
- data/test/system/workarea/storefront/kount_logged_in_checkout_system_test.rb +102 -0
- data/test/test_helper.rb +16 -0
- data/test/vcr_cassettes/kount/basic_fraud_test.yml +43 -0
- data/test/vcr_cassettes/kount/integration/moneris_new_credit_card.yml +84 -0
- data/test/vcr_cassettes/kount/integration/moneris_saved_credit_card.yml +84 -0
- data/test/vcr_cassettes/kount/order_fraud_update.yml +82 -0
- data/test/workers/workarea/kount/clean_orders_test.rb +40 -0
- data/test/workers/workarea/kount/process_review_order_test.rb +100 -0
- data/workarea-kount.gemspec +17 -0
- metadata +238 -0
data/LICENSE.md
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Workarea Kount
|
|
2
|
+
|
|
3
|
+
Kount plugin for the Workarea platform.
|
|
4
|
+
|
|
5
|
+
## Kount Response Codes
|
|
6
|
+
|
|
7
|
+
Kount's Risk Inquiry Services will return one of three values.
|
|
8
|
+
|
|
9
|
+
Approved - the order is not suspected as fraud
|
|
10
|
+
|
|
11
|
+
Decline - the order is fraud
|
|
12
|
+
|
|
13
|
+
Hold for manager review - the order is suspected fraud
|
|
14
|
+
|
|
15
|
+
## Kount Order Flow
|
|
16
|
+
|
|
17
|
+
### In Checkout
|
|
18
|
+
|
|
19
|
+
The Kount plugin sends the credit card and paypal orders to Kount before payment
|
|
20
|
+
is captured. Based on the response from Kount different actions will be taken.
|
|
21
|
+
|
|
22
|
+
#### Approved - The order is not fraud
|
|
23
|
+
|
|
24
|
+
The order is placed.
|
|
25
|
+
|
|
26
|
+
#### Decline - The order is fraud
|
|
27
|
+
|
|
28
|
+
An error is shown to the customer and redirected back to the payment step.
|
|
29
|
+
|
|
30
|
+
#### Hold for manager review - the order is suspected fraud
|
|
31
|
+
|
|
32
|
+
The credit card is tokenized and inventory is captured but the payment is not
|
|
33
|
+
authorized/purchased. The customer is shown the order confirmation as if the
|
|
34
|
+
order is placed. A Kount admin user needs to use approve or decline the order
|
|
35
|
+
via the Kount admin.
|
|
36
|
+
|
|
37
|
+
The Kount Event Notificaion System (ENS) will post the data back to Workarea.
|
|
38
|
+
|
|
39
|
+
If the admin declines the order in Kount, the order is canceled and the customer
|
|
40
|
+
recieves a cancelation email.
|
|
41
|
+
|
|
42
|
+
If the admin approves the order, the payment is attemped to be captured. If the
|
|
43
|
+
payment captures successfully the order is placed. If the payment fails
|
|
44
|
+
authorization/purchase, the order is canceled and the customer recives a
|
|
45
|
+
cancelation email.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Add values for `merchant_id`, `version`, `key`, and `ksalt` to your application secrets
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
kount:
|
|
53
|
+
merchant_id:
|
|
54
|
+
version:
|
|
55
|
+
key:
|
|
56
|
+
ksalt:
|
|
57
|
+
ens_username: username for basic auth for ENS
|
|
58
|
+
ens_password: password for basic auth for ENS
|
|
59
|
+
kount_site: <site name for ENS>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Event Notification System Configuration
|
|
63
|
+
|
|
64
|
+
The Kount ENS requires configuration in Workarea to match configuration in Kount.
|
|
65
|
+
If the ENS is not configured properly, Kount review decisions will not be
|
|
66
|
+
received by Workarea.
|
|
67
|
+
|
|
68
|
+
Workarea uses basic auth credentials `ens_username:ens_password` to authenticate
|
|
69
|
+
requests from the Kount ENS. If your application is behind basic_auth (e.g. in
|
|
70
|
+
staging or QA) you should configure these credentials to match your basic auth
|
|
71
|
+
credentials for the environment. If your application is not behind basic auth
|
|
72
|
+
these credentials can be anything you want, as long as they are secure.
|
|
73
|
+
|
|
74
|
+
#### Kount Site
|
|
75
|
+
|
|
76
|
+
The `kount_site` configuration is used by kount ENS to determine which endpoint
|
|
77
|
+
events should be sent to. If no `kount_site` is provided the DEFAULT site will
|
|
78
|
+
be used. `kount_site` can be used to configure different ENS endpoints for
|
|
79
|
+
testing inQA and Staging, or for multi-site implementations.
|
|
80
|
+
|
|
81
|
+
#### Configure ENS in Kount
|
|
82
|
+
|
|
83
|
+
In the Kount admin navigate to: Fraud Control -> Websites -> Add Website
|
|
84
|
+
|
|
85
|
+
The ENS Api URL should look like `https://ENS_USERNAME:ENS_PASSWORD@WEBSITE_DOMAIN/kount_orders`
|
|
86
|
+
|
|
87
|
+
Example: `https://kount:aksjei8243d8@www.example.com/kount_orders`
|
|
88
|
+
|
|
89
|
+
## Workarea Platform Documentation
|
|
90
|
+
|
|
91
|
+
See [http://developer.workarea.com](http://developer.workarea.com) for Workarea
|
|
92
|
+
platform documentation.
|
|
93
|
+
|
|
94
|
+
## Copyright & Licensing
|
|
95
|
+
|
|
96
|
+
Copyright WebLinc 2018. All rights reserved.
|
|
97
|
+
|
|
98
|
+
For licensing, contact sales@weblinc.com.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
|
|
10
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
11
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
12
|
+
rdoc.title = 'Tasker'
|
|
13
|
+
rdoc.options << '--line-numbers'
|
|
14
|
+
rdoc.rdoc_files.include('README.md')
|
|
15
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
|
19
|
+
load 'rails/tasks/engine.rake'
|
|
20
|
+
load 'rails/tasks/statistics.rake'
|
|
21
|
+
load 'workarea/changelog.rake'
|
|
22
|
+
|
|
23
|
+
require 'rake/testtask'
|
|
24
|
+
|
|
25
|
+
Rake::TestTask.new(:test) do |t|
|
|
26
|
+
t.libs << 'lib'
|
|
27
|
+
t.libs << 'test'
|
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
|
29
|
+
t.verbose = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
task default: :test
|
|
33
|
+
|
|
34
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
|
35
|
+
require 'workarea/kount/version'
|
|
36
|
+
|
|
37
|
+
desc "Release version #{Workarea::Kount::VERSION} of the gem"
|
|
38
|
+
task :release do
|
|
39
|
+
host = "https://#{ENV['BUNDLE_GEMS__WORKAREA__COM']}@gems.workarea.com"
|
|
40
|
+
|
|
41
|
+
Rake::Task['workarea:changelog'].execute
|
|
42
|
+
system 'git add CHANGELOG.md'
|
|
43
|
+
system 'git commit -m "Update CHANGELOG"'
|
|
44
|
+
system 'git push origin HEAD'
|
|
45
|
+
|
|
46
|
+
system "git tag -a v#{Workarea::Kount::VERSION} -m 'Tagging #{Workarea::Kount::VERSION}'"
|
|
47
|
+
system 'git push --tags'
|
|
48
|
+
|
|
49
|
+
system 'gem build workarea-kount.gemspec'
|
|
50
|
+
system "gem push workarea-kount-#{Workarea::Kount::VERSION}.gem"
|
|
51
|
+
system "gem push workarea-kount-#{Workarea::Kount::VERSION}.gem --host #{host}"
|
|
52
|
+
system "rm workarea-kount-#{Workarea::Kount::VERSION}.gem"
|
|
53
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
decorate Storefront::Checkout::PlaceOrderController, with: :kount do
|
|
3
|
+
def try_place_order
|
|
4
|
+
if current_checkout.place_order || current_checkout.handle_kount_review
|
|
5
|
+
completed_place_order
|
|
6
|
+
else
|
|
7
|
+
incomplete_place_order
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Storefront
|
|
3
|
+
class KountController < Workarea::Storefront::ApplicationController
|
|
4
|
+
def data_collector
|
|
5
|
+
current_order.update_attributes(kount_session_id: kount_session_id)
|
|
6
|
+
|
|
7
|
+
params = { merchant_id: Workarea::Kount.merchant_id, s: kount_session_id }
|
|
8
|
+
|
|
9
|
+
redirect_to "#{Workarea::Kount.data_collector_url}logo.htm?#{params.to_query}", status: 302
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def kount_session_id
|
|
15
|
+
@kount_session_id ||= "#{current_order.id}#{Time.now.to_i}R#{rand(10000..99999)}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Storefront
|
|
3
|
+
class KountOrdersController < ApplicationController
|
|
4
|
+
skip_before_action :verify_authenticity_token, only: [:bulk]
|
|
5
|
+
before_action :authenticate
|
|
6
|
+
|
|
7
|
+
def bulk
|
|
8
|
+
event_batch = Kount::EventBatch.new(request.raw_post)
|
|
9
|
+
|
|
10
|
+
if event_batch.valid?
|
|
11
|
+
event_batch.process!
|
|
12
|
+
successful_response
|
|
13
|
+
else
|
|
14
|
+
unsuccessful_response
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def authenticate
|
|
21
|
+
authenticated = authenticate_with_http_basic do |username, password|
|
|
22
|
+
Kount.credentials[:ens_username].present? &&
|
|
23
|
+
Kount.credentials[:ens_password].present? &&
|
|
24
|
+
username == Kount.credentials[:ens_username] &&
|
|
25
|
+
password == Kount.credentials[:ens_password]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unsuccessful_response unless authenticated
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def successful_response
|
|
32
|
+
render json: { status: 200 }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def unsuccessful_response
|
|
36
|
+
head :bad_request
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
decorate Storefront::OrderMailer, with: :kount do
|
|
3
|
+
def hold_denial(order_id)
|
|
4
|
+
order = Order.find(order_id)
|
|
5
|
+
@order = Storefront::OrderViewModel.new(order)
|
|
6
|
+
@recommendations = Storefront::EmailRecommendationsViewModel.wrap(order)
|
|
7
|
+
|
|
8
|
+
mail(
|
|
9
|
+
to: @order.email,
|
|
10
|
+
subject: t('workarea.storefront.email.hold_denial.subject', order_id: @order.id)
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
decorate Checkout, with: :kount do
|
|
3
|
+
def handle_kount_review
|
|
4
|
+
return false unless order.under_review?
|
|
5
|
+
return false unless complete?
|
|
6
|
+
return false unless shippable?
|
|
7
|
+
return false unless payable?
|
|
8
|
+
|
|
9
|
+
inventory.purchase
|
|
10
|
+
return false unless inventory.captured?
|
|
11
|
+
|
|
12
|
+
return true unless payment.uses_credit_card?
|
|
13
|
+
store_credit_card
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def handle_kount_approved
|
|
17
|
+
unless payment_collection.purchase
|
|
18
|
+
captured_inventory&.rollback
|
|
19
|
+
return false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
result = order.place
|
|
23
|
+
place_order_side_effects if result
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def captured_inventory
|
|
30
|
+
@captured_inventory ||= Inventory::Transaction.captured_for_order(order.id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def store_credit_card
|
|
34
|
+
Payment::StoreCreditCard.new(payment.credit_card).save!
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Workarea
|
|
4
|
+
class Checkout
|
|
5
|
+
module Fraud
|
|
6
|
+
class KountAnalyzer < Analyzer
|
|
7
|
+
# This is the `:mode` value used in the RIS Inquiry request to
|
|
8
|
+
# Kount. It tells Kount that this is a new order, and not an
|
|
9
|
+
# update to an existing order as used in the updater service
|
|
10
|
+
# class.
|
|
11
|
+
#
|
|
12
|
+
# @return [String]
|
|
13
|
+
MODE_Q = 'Q'
|
|
14
|
+
|
|
15
|
+
delegate :gateway, to: Workarea::Kount
|
|
16
|
+
delegate :kount_mode, :kount_request_user_defined_fields, to: :checkout
|
|
17
|
+
delegate :decision, to: :kount_response
|
|
18
|
+
|
|
19
|
+
# Tell Kount about the order and record its response.
|
|
20
|
+
#
|
|
21
|
+
# @return [Workarea::Order::FraudDecision]
|
|
22
|
+
def make_decision
|
|
23
|
+
kount_order.update_attributes!(response: kount_response)
|
|
24
|
+
|
|
25
|
+
Workarea::Order::FraudDecision.new(
|
|
26
|
+
decision: decision,
|
|
27
|
+
message: message
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Data for this Order from Kount.
|
|
32
|
+
#
|
|
33
|
+
# @return [Workarea::Kount::Order]
|
|
34
|
+
def kount_order
|
|
35
|
+
@kount_order ||= Workarea::Kount::Order.find_or_initialize_by(id: order.id)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Build the RIS inquiry request.
|
|
41
|
+
#
|
|
42
|
+
# @private
|
|
43
|
+
# @return [Hash]
|
|
44
|
+
def inquiry
|
|
45
|
+
Workarea::Kount::RisInquiry.new(
|
|
46
|
+
order: order,
|
|
47
|
+
payment: payment,
|
|
48
|
+
shippings: shippings,
|
|
49
|
+
mode: MODE_Q
|
|
50
|
+
).to_h
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Response for the Kount RIS inquiry request.
|
|
54
|
+
#
|
|
55
|
+
# @private
|
|
56
|
+
# @return [Workarea::Kount::Response]
|
|
57
|
+
def kount_response
|
|
58
|
+
@kount_response ||= gateway.call(inquiry)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Translated message for the Kount decision.
|
|
62
|
+
#
|
|
63
|
+
# @private
|
|
64
|
+
# @return [String]
|
|
65
|
+
def message
|
|
66
|
+
I18n.t(decision, scope: 'workarea.storefront.fraud.kount')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
module Kount
|
|
3
|
+
class Order
|
|
4
|
+
include ApplicationDocument
|
|
5
|
+
|
|
6
|
+
field :response, type: Workarea::Kount::Response
|
|
7
|
+
field :update_response, type: Workarea::Kount::Response
|
|
8
|
+
|
|
9
|
+
embeds_many :ens_events, class_name: 'Workarea::Kount::EnsEvent'
|
|
10
|
+
|
|
11
|
+
index(created_at: -1)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Workarea
|
|
2
|
+
decorate Order, with: :kount do
|
|
3
|
+
decorated do
|
|
4
|
+
field :kount_session_id, type: String
|
|
5
|
+
field :kount_decision, type: Symbol
|
|
6
|
+
|
|
7
|
+
scope :not_under_review, -> { where(:kount_decision.ne => :review) }
|
|
8
|
+
|
|
9
|
+
before_save :set_kount_decision, if: :fraud_decision?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class_methods do
|
|
13
|
+
# Query for orders which have expired in checkout, meaning they have been
|
|
14
|
+
# not been placed or updated for longer than the
|
|
15
|
+
# +Workarea.config.order_expiration_period+, but have started
|
|
16
|
+
# checkout. Contrast this with +Order.expired+, which does not
|
|
17
|
+
# factor in orders that have started checkout.
|
|
18
|
+
#
|
|
19
|
+
# Doesn't include orders under review or declined by kount
|
|
20
|
+
#
|
|
21
|
+
# @return [Mongoid::Criteria]
|
|
22
|
+
def expired_in_checkout
|
|
23
|
+
super.nin(kount_decision: [:review, :decline])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Overriding method from core/models/order/queries.rb module
|
|
27
|
+
def need_reminding
|
|
28
|
+
super.where(kount_decision: nil)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Find a current cart for a session. Returns a new order if one cannot be found.
|
|
32
|
+
#
|
|
33
|
+
# @param params [Hash]
|
|
34
|
+
# @return [Order]
|
|
35
|
+
#
|
|
36
|
+
def find_current(params = {})
|
|
37
|
+
if params[:id].present?
|
|
38
|
+
Order.not_placed.not_under_review.find(params[:id].to_s)
|
|
39
|
+
elsif params[:user_id].present?
|
|
40
|
+
Order.recently_updated.not_placed.not_under_review.find_by(params.slice(:user_id))
|
|
41
|
+
else
|
|
42
|
+
Order.new(user_id: params[:user_id])
|
|
43
|
+
end
|
|
44
|
+
rescue Mongoid::Errors::DocumentNotFound
|
|
45
|
+
Order.new(user_id: params[:user_id])
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Whether the FraudDecision#decision is `:review`.
|
|
50
|
+
def under_review?
|
|
51
|
+
!placed? && !canceled? && kount_decision == :review
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def abandoned?
|
|
55
|
+
super && kount_decision.nil?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def set_kount_decision
|
|
59
|
+
self.kount_decision = fraud_decision.decision
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|