workarea-forter 1.2.2

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.
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