workarea-forter 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.gitignore +17 -0
  7. data/CHANGELOG.md +63 -0
  8. data/CODE_OF_CONDUCT.md +3 -0
  9. data/CONTRIBUTING.md +3 -0
  10. data/Gemfile +25 -0
  11. data/LICENSE +52 -0
  12. data/LICENSE.md +3 -0
  13. data/README.md +113 -0
  14. data/Rakefile +60 -0
  15. data/app/controllers/workarea/admin/orders_controller.decorator +6 -0
  16. data/app/controllers/workarea/storefront/application_controller.decorator +10 -0
  17. data/app/models/search/admin/order.decorator +7 -0
  18. data/app/models/workarea/checkout/collect_payment.decorator +75 -0
  19. data/app/models/workarea/forter/decision.rb +18 -0
  20. data/app/models/workarea/forter/response.rb +15 -0
  21. data/app/models/workarea/fulfillment.decorator +77 -0
  22. data/app/models/workarea/order/status/suspected_fraud.rb +13 -0
  23. data/app/models/workarea/order.decorator +14 -0
  24. data/app/models/workarea/payment/saved_credit_card.decorator +14 -0
  25. data/app/models/workarea/payment/status/suspected_fraud.rb +13 -0
  26. data/app/models/workarea/payment/tender/credit_card.decorator +25 -0
  27. data/app/models/workarea/payment.decorator +13 -0
  28. data/app/services/workarea/forter/account.rb +42 -0
  29. data/app/services/workarea/forter/order.rb +145 -0
  30. data/app/services/workarea/forter/payment.rb +80 -0
  31. data/app/services/workarea/forter/tender/credit_card.rb +73 -0
  32. data/app/services/workarea/forter/tender/general.rb +29 -0
  33. data/app/services/workarea/forter/tender/gift_card.rb +23 -0
  34. data/app/services/workarea/forter/tender/paypal.rb +38 -0
  35. data/app/services/workarea/forter/tender/store_credit.rb +23 -0
  36. data/app/view_models/workarea/admin/order_view_model.decorator +7 -0
  37. data/app/views/workarea/admin/orders/_forter.html.haml +18 -0
  38. data/app/views/workarea/admin/orders/forter.html.haml +57 -0
  39. data/app/views/workarea/storefront/_forter_tracking.html.haml +4 -0
  40. data/app/workers/forter/update_status.rb +21 -0
  41. data/app/workers/workarea/save_user_order_details.decorator +34 -0
  42. data/bin/rails +20 -0
  43. data/config/initializers/appends.rb +9 -0
  44. data/config/initializers/workarea.rb +22 -0
  45. data/config/locales/en.yml +26 -0
  46. data/config/routes.rb +7 -0
  47. data/lib/workarea/forter/bogus_gateway.rb +62 -0
  48. data/lib/workarea/forter/decision_response.rb +60 -0
  49. data/lib/workarea/forter/engine.rb +8 -0
  50. data/lib/workarea/forter/gateway.rb +66 -0
  51. data/lib/workarea/forter/version.rb +5 -0
  52. data/lib/workarea/forter.rb +50 -0
  53. data/test/dummy/Rakefile +6 -0
  54. data/test/dummy/bin/bundle +3 -0
  55. data/test/dummy/bin/rails +4 -0
  56. data/test/dummy/bin/rake +4 -0
  57. data/test/dummy/bin/setup +30 -0
  58. data/test/dummy/bin/update +26 -0
  59. data/test/dummy/bin/yarn +11 -0
  60. data/test/dummy/config/application.rb +30 -0
  61. data/test/dummy/config/boot.rb +5 -0
  62. data/test/dummy/config/cable.yml +10 -0
  63. data/test/dummy/config/environment.rb +5 -0
  64. data/test/dummy/config/environments/development.rb +51 -0
  65. data/test/dummy/config/environments/production.rb +88 -0
  66. data/test/dummy/config/environments/test.rb +44 -0
  67. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  68. data/test/dummy/config/initializers/assets.rb +14 -0
  69. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  70. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  71. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  72. data/test/dummy/config/initializers/inflections.rb +16 -0
  73. data/test/dummy/config/initializers/mime_types.rb +4 -0
  74. data/test/dummy/config/initializers/workarea.rb +5 -0
  75. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  76. data/test/dummy/config/locales/en.yml +33 -0
  77. data/test/dummy/config/puma.rb +56 -0
  78. data/test/dummy/config/routes.rb +5 -0
  79. data/test/dummy/config/secrets.yml +32 -0
  80. data/test/dummy/config/spring.rb +6 -0
  81. data/test/dummy/config.ru +5 -0
  82. data/test/dummy/db/seeds.rb +2 -0
  83. data/test/dummy/log/.keep +0 -0
  84. data/test/factories/forter.rb +132 -0
  85. data/test/integration/workarea/admin/forter_order_integration_test.rb +36 -0
  86. data/test/integration/workarea/forter_authorize_cim_response_code_integration_test.rb +69 -0
  87. data/test/integration/workarea/forter_braintree_response_code_integration_test.rb +69 -0
  88. data/test/integration/workarea/forter_checkoutdotcom_integration_test.rb +71 -0
  89. data/test/integration/workarea/forter_cybersource_response_code_integration_test.rb +69 -0
  90. data/test/integration/workarea/forter_moneris_response_code_integration_test.rb +69 -0
  91. data/test/integration/workarea/forter_payflow_pro_response_code_test.rb +69 -0
  92. data/test/integration/workarea/storefront/forter_integration_test.rb +138 -0
  93. data/test/lib/workarea/forter/gateway_test.rb +47 -0
  94. data/test/models/workarea/checkout/forter_collect_payment_test.rb +60 -0
  95. data/test/models/workarea/forter_payment_test.rb +58 -0
  96. data/test/models/workarea/payment/forter_credit_card_test.rb +19 -0
  97. data/test/models/workarea/payment/forter_saved_credit_card_test.rb +32 -0
  98. data/test/services/workarea/forter/order_test.rb +82 -0
  99. data/test/support/workarea/forter_api_config.rb +27 -0
  100. data/test/system/workarea/admin/forter_system_test.rb +31 -0
  101. data/test/system/workarea/storefront/forter_braintree_checkout_system_test.rb +79 -0
  102. data/test/system/workarea/storefront/forter_tracking_system_test.rb +20 -0
  103. data/test/system/workarea/storefront/guest_checkout_system_test.decorator +43 -0
  104. data/test/teaspoon_env.rb +6 -0
  105. data/test/test_helper.rb +11 -0
  106. data/test/vcr_cassettes/forter/get_decision.yml +66 -0
  107. data/test/vcr_cassettes/forter/integration/authorize_net_response_code.yml +410 -0
  108. data/test/vcr_cassettes/forter/integration/braintree_response_code.yml +231 -0
  109. data/test/vcr_cassettes/forter/integration/checkoutdotcom_response_code.yml +129 -0
  110. data/test/vcr_cassettes/forter/integration/cyber_response_code.yml +199 -0
  111. data/test/vcr_cassettes/forter/integration/moneris_response_code.yml +84 -0
  112. data/test/vcr_cassettes/forter/integration/payflow_pro_response_code.yml +60 -0
  113. data/test/vcr_cassettes/forter/system/braintree_approved.yml +293 -0
  114. data/test/vcr_cassettes/forter/system/braintree_declined.yml +485 -0
  115. data/test/vcr_cassettes/forter/system/braintree_not_reviewed.yml +293 -0
  116. data/test/vcr_cassettes/forter/update_status.yml +182 -0
  117. data/test/view_models/workarea/storefront/order_view_model_test.decorator +10 -0
  118. data/test/workers/forter/update_status_test.rb +22 -0
  119. data/workarea-forter.gemspec +18 -0
  120. metadata +195 -0
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ class Order
3
+ module Status
4
+ class SuspectedFraud
5
+ include StatusCalculator::Status
6
+
7
+ def in_status?
8
+ !order.placed? && order.flagged_for_fraud? && !order.canceled?
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ decorate Order, with: :forter do
3
+ decorated do
4
+ field :forter_tracking_code, type: String
5
+ end
6
+
7
+ def flagged_for_fraud?
8
+ decision = Workarea::Forter::Decision.find(id) rescue nil
9
+ return false unless decision.present? && decision.response.present?
10
+
11
+ decision.response.action == 'decline'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ decorate Payment::SavedCreditCard, with: :forter do
3
+ decorated do
4
+ field :bin, type: String
5
+ before_validation :set_bin
6
+ end
7
+
8
+ def set_bin
9
+ if number.present?
10
+ self.bin = ActiveMerchant::Billing::CreditCard.first_digits(number)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ class Payment
3
+ module Status
4
+ class SuspectedFraud
5
+ include Workarea::StatusCalculator::Status
6
+
7
+ def in_status?
8
+ order.flagged_for_fraud?
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Workarea
2
+ decorate Payment::Tender::CreditCard, with: :forter do
3
+ decorated do
4
+ field :bin, type: String
5
+ before_validation :set_bin
6
+ end
7
+
8
+ def set_bin
9
+ if number.present?
10
+ self.bin = ActiveMerchant::Billing::CreditCard.first_digits(number)
11
+ end
12
+ end
13
+
14
+ def set_saved_card_values
15
+ if saved_card.present?
16
+ self.display_number = saved_card.display_number
17
+ self.issuer = saved_card.issuer
18
+ self.month = saved_card.month
19
+ self.year = saved_card.year
20
+ self.token = saved_card.token
21
+ self.bin = saved_card.bin
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ decorate Payment, with: :forter do
3
+ decorated do
4
+ field :flagged_for_fraud, type: Boolean, default: false
5
+ end
6
+
7
+ def rollback!(options = {})
8
+ transactions = tenders.flat_map(&:transactions)
9
+ operation = Workarea::Payment::Operation.new(transactions, options)
10
+ operation.rollback!
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ module Workarea
2
+ module Forter
3
+ class Account
4
+ attr_reader :order, :options
5
+
6
+ def initialize(order, options = {})
7
+ @order = order
8
+ @options = options
9
+ end
10
+
11
+ # @return Hash
12
+ def to_h
13
+ return guest_account unless order.user_id.present?
14
+
15
+ user_account
16
+ end
17
+
18
+ private
19
+
20
+ def user_account
21
+ user = Workarea::User.find(order.user_id)
22
+ {
23
+ firstName: user.first_name,
24
+ lastName: user.last_name,
25
+ email: user.email,
26
+ accountId: user.id.to_s,
27
+ created: user.created_at.to_i,
28
+ lastLoginIP: user.ip_address
29
+ }
30
+ end
31
+
32
+ def guest_account
33
+ payment = Workarea::Payment.find(order.id)
34
+ {
35
+ firstName: payment.address.first_name,
36
+ lastName: payment.address.last_name,
37
+ email: order.email
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,145 @@
1
+ module Workarea
2
+ module Forter
3
+ class Order
4
+ attr_reader :order, :options
5
+
6
+ def initialize(order, options = {})
7
+ @order = order
8
+ @options = options
9
+ end
10
+
11
+ # @return Hash
12
+ def to_h
13
+ hsh = {
14
+ orderId: order.id,
15
+ orderType: 'WEB',
16
+ timeSentToForter: forter_export_time,
17
+ checkoutTime: order.placed_at.to_i,
18
+ primaryRecipient: {
19
+ personalDetails: {
20
+ firstName: payment.address.first_name,
21
+ lastName: payment.address.last_name
22
+ },
23
+ address: primary_recipient_address,
24
+ phone: [
25
+ {
26
+ phone: payment.address.phone_number.to_s
27
+ }
28
+ ]
29
+ },
30
+ totalAmount: {
31
+ amountUSD: order.total_price.to_s
32
+ },
33
+ connectionInformation: {
34
+ customerIP: order.ip_address,
35
+ userAgent: order.user_agent,
36
+ forterTokenCookie: order.forter_tracking_code
37
+ },
38
+ primaryDeliveryDetails: {
39
+ deliveryType: delivery_type,
40
+ deliveryMethod: delivery_method,
41
+ deliveryPrice: {
42
+ amountUSD: order.shipping_total.to_s
43
+ },
44
+ carrier: shipping_service_carrier
45
+ },
46
+ cartItems: items,
47
+ payment: forter_payments,
48
+ accountOwner: forter_account,
49
+ totalDiscount: forter_discount_codes
50
+ }
51
+ end
52
+
53
+ private
54
+
55
+ def forter_export_time
56
+ @forter_export_time = Time.new.to_i * 1000
57
+ end
58
+
59
+ def delivery_type
60
+ return "DIGITAL" if order.items.all? { |oi| oi.digital? }
61
+ return "HYBRID" if order.items.any? { |oi| oi.digital? }
62
+ "PHYSICAL"
63
+ end
64
+
65
+ def delivery_method
66
+ return "EMAIL" if delivery_type == "DIGITAL"
67
+ shipping_service_name
68
+ end
69
+
70
+ def items
71
+ order.items.map do |oi|
72
+ item = Workarea::Storefront::OrderItemViewModel.new(oi)
73
+ {
74
+ basicItemData: {
75
+ name: item.product.name,
76
+ quantity: item.quantity,
77
+ type: item.digital? ? "NON_TANGIBLE" : "TANGIBLE",
78
+ price: { amountUSD: item.total_value.to_s },
79
+ productId: item.product.id
80
+ }
81
+ }
82
+ end
83
+ end
84
+
85
+ def forter_payments
86
+ Forter::Payment.new(payment).to_a
87
+ end
88
+
89
+ def forter_account
90
+ Forter::Account.new(order).to_h
91
+ end
92
+
93
+ def forter_discount_codes
94
+ return unless order.promo_codes.present?
95
+
96
+ # Forter only supports 1 entry in the totalDiscount field.
97
+ code = order.promo_codes.first
98
+ {
99
+ couponCodeUsed: code,
100
+ discountType: "PROMOCODE"
101
+ }
102
+ end
103
+
104
+ def payment
105
+ @payment ||= Workarea::Payment.find(order.id)
106
+ end
107
+
108
+ def shipping
109
+ @shipping ||= Workarea::Shipping.find_by_order(order.id)
110
+ end
111
+
112
+ def shipping_service
113
+ return unless shipping.present? && shipping.shipping_service.present?
114
+ shipping.shipping_service
115
+ end
116
+
117
+ def shipping_service_name
118
+ return unless shipping_service.present?
119
+ shipping_service.name
120
+ end
121
+
122
+ def shipping_service_carrier
123
+ return unless shipping_service.present?
124
+ shipping_service.carrier
125
+ end
126
+
127
+ def primary_recipient_address
128
+ address_obj = if shipping.present?
129
+ shipping.address
130
+ else
131
+ payment.address
132
+ end
133
+
134
+ {
135
+ address1: address_obj.street,
136
+ city: address_obj.city,
137
+ country: address_obj.country.alpha2,
138
+ address2: address_obj.street_2,
139
+ zip: address_obj.postal_code,
140
+ region: address_obj.region
141
+ }
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,80 @@
1
+ module Workarea
2
+ module Forter
3
+ class Payment
4
+ attr_reader :payment, :options
5
+
6
+ def initialize(payment, options = {})
7
+ @payment = payment
8
+ @options = options
9
+ end
10
+
11
+ # @return Array
12
+ def to_a
13
+ payment.tenders.map do | tender|
14
+
15
+ tender_hash = build_tender_details(tender)
16
+
17
+ billing_details_hash = {
18
+ billingDetails: {
19
+ personalDetails: {
20
+ firstName: address.first_name,
21
+ lastName: address.last_name
22
+ },
23
+ address: forter_address,
24
+ phone: [{ phone: address.phone_number.to_s }]
25
+ },
26
+ amount: { amountUSD: tender.amount.to_s }
27
+ }
28
+
29
+ tender_hash.merge!(billing_details_hash)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def build_tender_details(tender)
36
+ case tender.slug
37
+ when :credit_card
38
+ Tender::CreditCard.new(tender).to_h
39
+ when :gift_card
40
+ Tender::GiftCard.new(tender).to_h
41
+ when :store_credit
42
+ Tender::StoreCredit.new(tender).to_h
43
+ when :paypal
44
+ Tender::Paypal.new(tender).to_h
45
+ else
46
+ Tender::General.new(tender).to_h
47
+ end
48
+ end
49
+
50
+ def address
51
+ payment.address
52
+ end
53
+
54
+ def forter_address
55
+ {
56
+ address1: address.street,
57
+ address2: address.street_2,
58
+ city: address.city,
59
+ country: address.country.alpha2,
60
+ zip: address.postal_code,
61
+ region: address.region
62
+ }
63
+ end
64
+
65
+ def credit_used
66
+ tender = payment.tenders.detect { |t| t.slug == :store_credit }
67
+ return {} unless tender.present?
68
+ {
69
+ creditUsed: {
70
+ amountUSD: tender.amount.to_s
71
+ }
72
+ }
73
+ end
74
+
75
+ def cc_month(month)
76
+ month < 10 ? "0#{month}" : month.to_s
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,73 @@
1
+ module Workarea
2
+ module Forter
3
+ module Tender
4
+ class CreditCard
5
+ attr_reader :tender, :options
6
+
7
+ def initialize(tender, options = {})
8
+ @tender = tender
9
+ @options = options
10
+ end
11
+
12
+ # @return Hash
13
+ def to_h
14
+ return {} unless tender.present?
15
+ {
16
+ creditCard: {
17
+ bin: tender.bin,
18
+ lastFourDigits: tender.display_number[-4, 4].to_s,
19
+ expirationMonth: cc_month(tender.month),
20
+ expirationYear: tender.year.to_s,
21
+ cardBrand: tender.issuer,
22
+ nameOnCard: "#{address.first_name} #{address.last_name}"
23
+ }.merge!(verification_results)
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ def address
30
+ payment.address
31
+ end
32
+
33
+ def cc_month(month)
34
+ month < 10 ? "0#{month}" : month.to_s
35
+ end
36
+
37
+ def payment
38
+ @payment ||= tender.payment
39
+ end
40
+
41
+ def transaction
42
+ @transaction ||= tender.transactions.sort_by { |t| t.created_at.to_i }.last
43
+ end
44
+
45
+ def verification_results
46
+ return {} unless tender.transactions.present?
47
+
48
+ return {} unless transaction.response.avs_result.present?
49
+ {
50
+ verificationResults: {
51
+ avsFullResult: transaction.response.avs_result["code"].to_s,
52
+ cvvResult: transaction.response.cvv_result["code"].to_s,
53
+ authorizationCode: transaction.response.authorization.to_s,
54
+ processorResponseText: transaction.response.message.to_s,
55
+ processorResponseCode: forter_authorization_code.to_s
56
+ }
57
+ }
58
+
59
+ rescue => e
60
+ Forter.log_error(e)
61
+ {
62
+ verificationResults: {}
63
+ }
64
+ end
65
+
66
+ def forter_authorization_code
67
+ gateway_class = Workarea.config.gateways.credit_card.class.to_s
68
+ Workarea.config.forter.response_code[gateway_class].call(transaction) rescue nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,29 @@
1
+ module Workarea
2
+ module Forter
3
+ module Tender
4
+ class General
5
+ attr_reader :tender, :options
6
+
7
+ def initialize(tender, options = {})
8
+ @tender = tender
9
+ @options = options
10
+ end
11
+
12
+ # @return Hash
13
+ def to_h
14
+ {
15
+ generalPaymentMethod: {
16
+ name: tender.slug.to_s,
17
+ payload: transaction.response.params
18
+ }
19
+ }
20
+ end
21
+
22
+ private
23
+ def transaction
24
+ @transaction ||= tender.transactions.sort_by { |t| t.created_at.to_i }.last
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Workarea
2
+ module Forter
3
+ module Tender
4
+ class GiftCard
5
+ attr_reader :tender, :options
6
+
7
+ def initialize(tender, options = {})
8
+ @tender = tender
9
+ @options = options
10
+ end
11
+
12
+ # @return Hash
13
+ def to_h
14
+ {
15
+ creditUsed: {
16
+ amountUSD: tender.amount.to_s
17
+ }
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ module Workarea
2
+ module Forter
3
+ module Tender
4
+ class Paypal
5
+
6
+ class PaypalDependencyError < StandardError; end
7
+ attr_reader :tender, :options
8
+
9
+ def initialize(tender, options = {})
10
+ @tender = tender
11
+ @options = options
12
+ end
13
+
14
+ # @return Hash
15
+ def to_h
16
+ raise PaypalDependencyError.new("Paypal plugin not installed but paypal transaction detected") unless Plugin.installed?("Workarea::Paypal")
17
+ {
18
+ paypal: {
19
+ payerId: tender.payer_id,
20
+ fullPaypalResponsePayload: transaction.response.params,
21
+ payerEmail: tender.details["payer"],
22
+ paymentStatus: transaction.response.params["payment_status"],
23
+ paymentMethod: transaction.response.params["payment_type"],
24
+ payerAddressStatus: tender.details["address_status"],
25
+ payerStatus: tender.details["payer_status"]
26
+ }
27
+ }
28
+ end
29
+
30
+ private
31
+
32
+ def transaction
33
+ @transaction ||= tender.transactions.detect { |t| t.success? }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module Workarea
2
+ module Forter
3
+ module Tender
4
+ class StoreCredit
5
+ attr_reader :tender, :options
6
+
7
+ def initialize(tender, options = {})
8
+ @tender = tender
9
+ @options = options
10
+ end
11
+
12
+ # @return Hash
13
+ def to_h
14
+ {
15
+ creditUsed: {
16
+ amountUSD: tender.amount.to_s
17
+ }
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Admin::OrderViewModel, with: :forter do
3
+ def decision
4
+ @decision = Workarea::Forter::Decision.find(model.id) rescue nil
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ - if order.decision.present?
2
+ .grid__cell
3
+ .card{ class: card_classes(:forter, local_assigns[:active]) }
4
+ = link_to forter_order_path(order), class: 'card__header' do
5
+ %span.card__header-text= t('workarea.admin.orders.cards.forter.title')
6
+ = inline_svg 'workarea/admin/icons/link.svg', class: 'card__icon'
7
+
8
+ - if local_assigns[:active].blank?
9
+ .card__body
10
+ %ul.list-reset
11
+ %li
12
+ %strong= t('workarea.admin.orders.cards.forter.decision')
13
+ = order.decision.response&.action
14
+
15
+ - if order.decision.response&.reason_code.present?
16
+ %li
17
+ %strong= t('workarea.admin.orders.cards.forter.reason_code')
18
+ = order.decision.response.reason_code
@@ -0,0 +1,57 @@
1
+ - @page_title = t('workarea.admin.orders.forter.page_title', id: @order.id)
2
+
3
+ .view
4
+ .view__header
5
+ .grid.grid--middle.grid--right
6
+ .grid__cell.grid__cell--50
7
+ .view__heading
8
+ = link_to_index_for(@order)
9
+ %h1= link_to @order.name, url_for(@order)
10
+ .grid__cell.grid__cell--25
11
+ = render_aux_navigation_for(@order)
12
+
13
+ .view__container
14
+ = render_cards_for(@order, :forter)
15
+
16
+ .view__container.view__container--narrow
17
+ .grid
18
+ .grid__cell.grid__cell--50
19
+ %h2= t('workarea.admin.orders.forter.title')
20
+ %ul.list-reset
21
+
22
+ %li
23
+ %strong= t('workarea.admin.orders.forter.status')
24
+ = @order.decision.external_order_status
25
+ %li
26
+ %br
27
+ %strong= link_to t('workarea.admin.orders.forter.console'), "https://portal.forter.com/dashboard/#{@order.id}"
28
+
29
+ .grid__cell.grid__cell--50
30
+ %h2= t('workarea.admin.orders.forter.details_heading')
31
+
32
+ %p= t('workarea.admin.orders.forter.details_description')
33
+
34
+ %table
35
+ %thead
36
+ %tr
37
+ %th= t('workarea.admin.orders.forter.details.action')
38
+ %th= t('workarea.admin.orders.forter.details.message')
39
+ %th= t('workarea.admin.orders.forter.details.reason_code')
40
+ %th= t('workarea.admin.orders.forter.details.decision_error')
41
+ %th= t('workarea.admin.orders.forter.error')
42
+
43
+ %tbody
44
+ - @order.decision.responses.each do |response|
45
+ %tr
46
+ %td= response.fraud_decision_action
47
+ %td= response.decision_response&.body_message
48
+ %td= response.decision_response&.reason_code
49
+ %td
50
+ -if response.decision_response&.errors.present?
51
+ - response.decision_response.errors.each do |e|
52
+ %li= e["message"]
53
+ %td= response.error
54
+
55
+
56
+
57
+
@@ -0,0 +1,4 @@
1
+ - if Workarea::Forter.site_id.present?
2
+ %script{type: "text/javascript", id: Workarea::Forter.site_id }
3
+ (function () {var siteId = "#{Workarea::Forter.site_id}";function t(t,e){for(var n=t.split(""),r=0;r<n.length;++r)n[r]=String.fromCharCode(n[r].charCodeAt(0)+e);return n.join("")}function e(e){return t(e,-l).replace(/%SN%/g,siteId)}function n(t){try{S.ex=t,g(S)}catch(e){}}function r(t,e,n){var r=document.createElement("script");r.onerror=n,r.onload=e,r.type="text/javascript",r.id="ftr__script",r.async=!0,r.src="https://"+t;var o=document.getElementsByTagName("script")[0];o.parentNode.insertBefore(r,o)}function o(){k(T.uAL),setTimeout(i,v,T.uAL)}function i(t){try{var e=t===T.uDF?h:m;r(e,function(){try{U(),n(t+T.uS)}catch(e){}},function(){try{U(),S.td=1*new Date-S.ts,n(t+T.uF),t===T.uDF&&o()}catch(e){n(T.eUoe)}})}catch(i){n(t+T.eTlu)}}var a={write:function(t,e,n,r){void 0===r&&(r=!0);var o,i;if(n?(o=new Date,o.setTime(o.getTime()+24*n*60*60*1e3),i="; expires="+o.toGMTString()):i="",!r)return void(document.cookie=escape(t)+"="+escape(e)+i+"; path=/");var a,c,u;if(u=location.host,1===u.split(".").length)document.cookie=escape(t)+"="+escape(e)+i+"; path=/";else{c=u.split("."),c.shift(),a="."+c.join("."),document.cookie=escape(t)+"="+escape(e)+i+"; path=/; domain="+a;var s=this.read(t);null!=s&&s==e||(a="."+u,document.cookie=escape(t)+"="+escape(e)+i+"; path=/; domain="+a)}},read:function(t){for(var e=escape(t)+"=",n=document.cookie.split(";"),r=0;r<n.length;r++){for(var o=n[r];" "==o.charAt(0);)o=o.substring(1,o.length);if(0===o.indexOf(e))return unescape(o.substring(e.length,o.length))}return null}},c="fort",u="erTo",s="ken",d=c+u+s,f="9";f+="ck";var l=3,h=e("(VQ(1fgq71iruwhu1frp2vq2(VQ(2vfulsw1mv"),m=e("g68x4yj4t5;e6z1forxgiurqw1qhw2vq2(VQ(2vfulsw1mv"),v=10;window.ftr__startScriptLoad=1*new Date;var g=function(t){var e=function(t){return t||""},n=e(t.id)+"_"+e(t.ts)+"_"+e(t.td)+"_"+e(t.ex)+"_"+e(f);a.write(d,n,1825,!0)},p=function(){var t=a.read(d)||"",e=t.split("_"),n=function(t){return e[t]||void 0};return{id:n(0),ts:n(1),td:n(2),ex:n(3),vr:n(4)}},w=function(){for(var t={},e="fgu",n=[],r=0;r<256;r++)n[r]=(r<16?"0":"")+r.toString(16);var o=function(t,e,r,o,i){var a=i?"-":"";return n[255&t]+n[t>>8&255]+n[t>>16&255]+n[t>>24&255]+a+n[255&e]+n[e>>8&255]+a+n[e>>16&15|64]+n[e>>24&255]+a+n[63&r|128]+n[r>>8&255]+a+n[r>>16&255]+n[r>>24&255]+n[255&o]+n[o>>8&255]+n[o>>16&255]+n[o>>24&255]},i=function(){if(window.Uint32Array&&window.crypto&&window.crypto.getRandomValues){var t=new window.Uint32Array(4);return window.crypto.getRandomValues(t),{d0:t[0],d1:t[1],d2:t[2],d3:t[3]}}return{d0:4294967296*Math.random()>>>0,d1:4294967296*Math.random()>>>0,d2:4294967296*Math.random()>>>0,d3:4294967296*Math.random()>>>0}},a=function(){var t="",e=function(t,e){for(var n="",r=t;r>0;--r)n+=e.charAt(1e3*Math.random()%e.length);return n};return t+=e(2,"0123456789"),t+=e(1,"123456789"),t+=e(8,"0123456789")};return t.safeGenerateNoDash=function(){try{var t=i();return o(t.d0,t.d1,t.d2,t.d3,!1)}catch(n){try{return e+a()}catch(n){}}},t.isValidNumericalToken=function(t){return t&&t.toString().length<=11&&t.length>=9&&parseInt(t,10).toString().length<=11&&parseInt(t,10).toString().length>=9},t.isValidUUIDToken=function(t){return t&&32===t.toString().length&&/^[a-z0-9]+$/.test(t)},t.isValidFGUToken=function(t){return 0==t.indexOf(e)&&t.length>=12},t}(),T={uDF:"UDF",uAL:"UAL",mLd:"1",eTlu:"2",eUoe:"3",uS:"4",uF:"9",tmos:["T5","T10","T15","T30","T60"],tmosSecs:[5,10,15,30,60],bIR:"43"},y=function(t,e){for(var n=T.tmos,r=0;r<n.length;r++)if(t+n[r]===e)return!0;return!1};try{var S=p();try{S.id&&(w.isValidNumericalToken(S.id)||w.isValidUUIDToken(S.id)||w.isValidFGUToken(S.id))||(S.id=w.safeGenerateNoDash()),S.ts=window.ftr__startScriptLoad,g(S);var D=new Array(T.tmosSecs.length),k=function(t){for(var e=0;e<T.tmosSecs.length;e++)D[e]=setTimeout(n,1e3*T.tmosSecs[e],t+T.tmos[e])},U=function(){for(var t=0;t<T.tmosSecs.length;t++)clearTimeout(D[t])};y(T.uDF,S.ex)?o():(k(T.uDF),setTimeout(i,v,T.uDF))}catch(F){n(T.mLd)}}catch(F){}})()
4
+
@@ -0,0 +1,21 @@
1
+ module Workarea
2
+ module Forter
3
+ class UpdateStatus
4
+ include Sidekiq::Worker
5
+
6
+ def perform(id, status_hash)
7
+ decision = Workarea::Forter::Decision.find(id) rescue nil
8
+
9
+ if decision.blank?
10
+ Rails.logger.warn "No decision record found for #{id} during update status"
11
+ return false
12
+ end
13
+
14
+ Workarea::Forter.gateway.update_order_status(id, status_hash)
15
+
16
+ decision.external_order_status = status_hash[:updatedStatus]
17
+ decision.save!
18
+ end
19
+ end
20
+ end
21
+ end