workarea-forter 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +20 -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/.gitignore +17 -0
- data/CHANGELOG.md +63 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +25 -0
- data/LICENSE +52 -0
- data/LICENSE.md +3 -0
- data/README.md +113 -0
- data/Rakefile +60 -0
- data/app/controllers/workarea/admin/orders_controller.decorator +6 -0
- data/app/controllers/workarea/storefront/application_controller.decorator +10 -0
- data/app/models/search/admin/order.decorator +7 -0
- data/app/models/workarea/checkout/collect_payment.decorator +75 -0
- data/app/models/workarea/forter/decision.rb +18 -0
- data/app/models/workarea/forter/response.rb +15 -0
- data/app/models/workarea/fulfillment.decorator +77 -0
- data/app/models/workarea/order/status/suspected_fraud.rb +13 -0
- data/app/models/workarea/order.decorator +14 -0
- data/app/models/workarea/payment/saved_credit_card.decorator +14 -0
- data/app/models/workarea/payment/status/suspected_fraud.rb +13 -0
- data/app/models/workarea/payment/tender/credit_card.decorator +25 -0
- data/app/models/workarea/payment.decorator +13 -0
- data/app/services/workarea/forter/account.rb +42 -0
- data/app/services/workarea/forter/order.rb +145 -0
- data/app/services/workarea/forter/payment.rb +80 -0
- data/app/services/workarea/forter/tender/credit_card.rb +73 -0
- data/app/services/workarea/forter/tender/general.rb +29 -0
- data/app/services/workarea/forter/tender/gift_card.rb +23 -0
- data/app/services/workarea/forter/tender/paypal.rb +38 -0
- data/app/services/workarea/forter/tender/store_credit.rb +23 -0
- data/app/view_models/workarea/admin/order_view_model.decorator +7 -0
- data/app/views/workarea/admin/orders/_forter.html.haml +18 -0
- data/app/views/workarea/admin/orders/forter.html.haml +57 -0
- data/app/views/workarea/storefront/_forter_tracking.html.haml +4 -0
- data/app/workers/forter/update_status.rb +21 -0
- data/app/workers/workarea/save_user_order_details.decorator +34 -0
- data/bin/rails +20 -0
- data/config/initializers/appends.rb +9 -0
- data/config/initializers/workarea.rb +22 -0
- data/config/locales/en.yml +26 -0
- data/config/routes.rb +7 -0
- data/lib/workarea/forter/bogus_gateway.rb +62 -0
- data/lib/workarea/forter/decision_response.rb +60 -0
- data/lib/workarea/forter/engine.rb +8 -0
- data/lib/workarea/forter/gateway.rb +66 -0
- data/lib/workarea/forter/version.rb +5 -0
- data/lib/workarea/forter.rb +50 -0
- data/test/dummy/Rakefile +6 -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 +30 -0
- data/test/dummy/bin/update +26 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config/application.rb +30 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +51 -0
- data/test/dummy/config/environments/production.rb +88 -0
- data/test/dummy/config/environments/test.rb +44 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -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/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +56 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/secrets.yml +32 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/log/.keep +0 -0
- data/test/factories/forter.rb +132 -0
- data/test/integration/workarea/admin/forter_order_integration_test.rb +36 -0
- data/test/integration/workarea/forter_authorize_cim_response_code_integration_test.rb +69 -0
- data/test/integration/workarea/forter_braintree_response_code_integration_test.rb +69 -0
- data/test/integration/workarea/forter_checkoutdotcom_integration_test.rb +71 -0
- data/test/integration/workarea/forter_cybersource_response_code_integration_test.rb +69 -0
- data/test/integration/workarea/forter_moneris_response_code_integration_test.rb +69 -0
- data/test/integration/workarea/forter_payflow_pro_response_code_test.rb +69 -0
- data/test/integration/workarea/storefront/forter_integration_test.rb +138 -0
- data/test/lib/workarea/forter/gateway_test.rb +47 -0
- data/test/models/workarea/checkout/forter_collect_payment_test.rb +60 -0
- data/test/models/workarea/forter_payment_test.rb +58 -0
- data/test/models/workarea/payment/forter_credit_card_test.rb +19 -0
- data/test/models/workarea/payment/forter_saved_credit_card_test.rb +32 -0
- data/test/services/workarea/forter/order_test.rb +82 -0
- data/test/support/workarea/forter_api_config.rb +27 -0
- data/test/system/workarea/admin/forter_system_test.rb +31 -0
- data/test/system/workarea/storefront/forter_braintree_checkout_system_test.rb +79 -0
- data/test/system/workarea/storefront/forter_tracking_system_test.rb +20 -0
- data/test/system/workarea/storefront/guest_checkout_system_test.decorator +43 -0
- data/test/teaspoon_env.rb +6 -0
- data/test/test_helper.rb +11 -0
- data/test/vcr_cassettes/forter/get_decision.yml +66 -0
- data/test/vcr_cassettes/forter/integration/authorize_net_response_code.yml +410 -0
- data/test/vcr_cassettes/forter/integration/braintree_response_code.yml +231 -0
- data/test/vcr_cassettes/forter/integration/checkoutdotcom_response_code.yml +129 -0
- data/test/vcr_cassettes/forter/integration/cyber_response_code.yml +199 -0
- data/test/vcr_cassettes/forter/integration/moneris_response_code.yml +84 -0
- data/test/vcr_cassettes/forter/integration/payflow_pro_response_code.yml +60 -0
- data/test/vcr_cassettes/forter/system/braintree_approved.yml +293 -0
- data/test/vcr_cassettes/forter/system/braintree_declined.yml +485 -0
- data/test/vcr_cassettes/forter/system/braintree_not_reviewed.yml +293 -0
- data/test/vcr_cassettes/forter/update_status.yml +182 -0
- data/test/view_models/workarea/storefront/order_view_model_test.decorator +10 -0
- data/test/workers/forter/update_status_test.rb +22 -0
- data/workarea-forter.gemspec +18 -0
- metadata +195 -0
@@ -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,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,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
|