flowcommerce_spree 0.0.3 → 0.0.4
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 +4 -4
- data/app/controllers/concerns/current_zone_loader_decorator.rb +5 -6
- data/app/controllers/flowcommerce_spree/inventory_controller.rb +23 -0
- data/app/controllers/flowcommerce_spree/orders_controller.rb +18 -0
- data/app/controllers/users/sessions_controller_decorator.rb +19 -2
- data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +0 -16
- data/app/models/spree/address_decorator.rb +1 -1
- data/app/models/spree/calculator/flow_io.rb +1 -1
- data/app/models/spree/flow_io_credit_card_decorator.rb +21 -0
- data/app/models/spree/{order_decorator.rb → flow_io_order_decorator.rb} +31 -65
- data/app/models/spree/gateway/flow_io.rb +61 -24
- data/app/models/spree/{credit_card_decorator.rb → payment_capture_event_decorator.rb} +1 -1
- data/app/serializers/api/v2/order_serializer_decorator.rb +20 -0
- data/app/services/flowcommerce_spree/import_experience_items.rb +1 -1
- data/app/services/flowcommerce_spree/order_sync.rb +26 -155
- data/app/services/flowcommerce_spree/order_updater.rb +76 -0
- data/app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb +21 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +6 -4
- data/lib/flow/simple_gateway.rb +0 -36
- data/lib/flowcommerce_spree.rb +3 -1
- data/lib/flowcommerce_spree/engine.rb +1 -1
- data/lib/flowcommerce_spree/logging_http_client.rb +29 -13
- data/lib/flowcommerce_spree/session.rb +0 -18
- data/lib/flowcommerce_spree/version.rb +1 -1
- data/lib/flowcommerce_spree/webhook_service.rb +74 -104
- metadata +10 -19
- data/app/models/spree/line_item_decorator.rb +0 -15
data/lib/flowcommerce_spree.rb
CHANGED
@@ -9,9 +9,11 @@ require 'flowcommerce_spree/logging_http_handler'
|
|
9
9
|
require 'flowcommerce_spree/webhook_service'
|
10
10
|
require 'flowcommerce_spree/session'
|
11
11
|
require 'flow/simple_gateway'
|
12
|
-
require 'request_store'
|
13
12
|
|
14
13
|
module FlowcommerceSpree
|
14
|
+
API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
|
15
|
+
ENV['FLOW_TOKEN'] = API_KEY
|
16
|
+
|
15
17
|
def self.client(logger: FlowcommerceSpree.logger, **opts)
|
16
18
|
FlowCommerce.instance(http_handler: LoggingHttpHandler.new(logger: logger), **opts)
|
17
19
|
end
|
@@ -24,7 +24,7 @@ module FlowcommerceSpree
|
|
24
24
|
|
25
25
|
app.config.flowcommerce_spree[:mounted_path] = ENV.fetch('FLOW_MOUNT_PATH', '/flow')
|
26
26
|
|
27
|
-
app.routes.
|
27
|
+
app.routes.prepend do
|
28
28
|
mount FlowcommerceSpree::Engine => app.config.flowcommerce_spree[:mounted_path]
|
29
29
|
end
|
30
30
|
end
|
@@ -15,31 +15,47 @@ module FlowcommerceSpree
|
|
15
15
|
|
16
16
|
start_time = Time.now.utc.round(10)
|
17
17
|
|
18
|
+
# Contrived example to show how client settings can be adjusted
|
18
19
|
# if request.path.start_with?('/organizations')
|
19
|
-
|
20
|
-
|
21
|
-
# client.read_timeout = 60
|
20
|
+
# client.open_timeout = 60
|
21
|
+
# client.read_timeout = 60
|
22
22
|
# end
|
23
23
|
|
24
24
|
begin
|
25
25
|
response = super
|
26
26
|
rescue Io::Flow::V0::HttpClient::ServerError => e
|
27
|
-
@error = { error: e }
|
27
|
+
@error = { error: Oj.load(e.body), code: e.code, status: e.details }
|
28
|
+
raise exception_to_raise(e), @error.dig(:error, 'messages')
|
28
29
|
ensure
|
29
30
|
# client.open_timeout = original_open
|
30
31
|
# client.read_timeout = original_read
|
31
32
|
|
32
|
-
|
33
|
+
log_request(request, response, start_time)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def exception_to_raise(flow_io_exception)
|
40
|
+
resource = flow_io_exception.uri.split('/').last
|
41
|
+
exception = case resource
|
42
|
+
when 'reversals', 'refunds'
|
43
|
+
'Spree::Core::GatewayError'
|
44
|
+
else
|
45
|
+
'StandardError'
|
46
|
+
end
|
47
|
+
exception.constantize
|
48
|
+
end
|
33
49
|
|
34
|
-
|
35
|
-
|
36
|
-
"headers: #{request.instance_variable_get(:@header)}\nbody: #{request.body}\n"\
|
37
|
-
"response: #{response&.force_encoding('utf-8')}\n"\
|
38
|
-
"Completed #{request.method} #{request.path} #{duration} ms\n"
|
39
|
-
)
|
50
|
+
def log_request(request, response, start_time)
|
51
|
+
duration = ((Time.now.utc.round(10) - start_time) * 1000).round(0)
|
40
52
|
|
41
|
-
|
42
|
-
|
53
|
+
@logger.info("Started #{request.method} #{request.path}\n"\
|
54
|
+
"headers: #{request.instance_variable_get(:@header)}\nbody: #{request.body}\n"\
|
55
|
+
"response: #{response&.force_encoding('utf-8')}\n"\
|
56
|
+
"Completed #{request.method} #{request.path} #{duration} ms\n")
|
57
|
+
|
58
|
+
@logger.info "Error: #{@error.inspect}" if @error
|
43
59
|
end
|
44
60
|
end
|
45
61
|
end
|
@@ -54,23 +54,5 @@ module FlowcommerceSpree
|
|
54
54
|
def id
|
55
55
|
@session.id
|
56
56
|
end
|
57
|
-
|
58
|
-
# because we do not get full experience from session, we have to get from exp list
|
59
|
-
def delivered_duty_options
|
60
|
-
return nil unless experience
|
61
|
-
|
62
|
-
return unless (flow_experience = Flow::Experience.get(experience.key))
|
63
|
-
|
64
|
-
Hashie::Mash.new(flow_experience.settings.delivered_duty.to_hash)
|
65
|
-
end
|
66
|
-
|
67
|
-
# if we have more than one choice, we show choice popup
|
68
|
-
def offers_delivered_duty_choice?
|
69
|
-
if (options = delivered_duty_options)
|
70
|
-
options.available.length > 1
|
71
|
-
else
|
72
|
-
false
|
73
|
-
end
|
74
|
-
end
|
75
57
|
end
|
76
58
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FlowcommerceSpree
|
4
|
-
#
|
4
|
+
# responds to webhook events from flow.io
|
5
5
|
class WebhookService
|
6
6
|
attr_accessor :errors
|
7
7
|
alias full_messages errors
|
@@ -17,30 +17,28 @@ module FlowcommerceSpree
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def process
|
20
|
-
|
21
|
-
hook_method = "hook_#{discriminator}"
|
20
|
+
hook_method = @data['discriminator']
|
22
21
|
# If hook processing method registered an error, a self.object of WebhookService with this error will be
|
23
22
|
# returned, else an ActiveRecord object will be returned
|
24
23
|
return __send__(hook_method) if respond_to?(hook_method, true)
|
25
24
|
|
26
|
-
errors << { message: "No hook for #{
|
25
|
+
errors << { message: "No hook for #{hook_method}" }
|
27
26
|
self
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
31
30
|
|
32
|
-
def
|
33
|
-
capture = @data['capture']
|
31
|
+
def capture_upserted_v2
|
32
|
+
errors << { message: 'Capture param missing' } && (return self) unless (capture = @data['capture']&.to_hash)
|
33
|
+
|
34
34
|
order_number = capture.dig('authorization', 'order', 'number')
|
35
35
|
if (order = Spree::Order.find_by(number: order_number))
|
36
36
|
order.flow_data['captures'] ||= []
|
37
37
|
order_captures = order.flow_data['captures']
|
38
|
-
order_captures.delete_if
|
39
|
-
c['id'] == capture['id']
|
40
|
-
end
|
38
|
+
order_captures.delete_if { |c| c['id'] == capture['id'] }
|
41
39
|
order_captures << capture
|
42
|
-
|
43
40
|
order.update_column(:meta, order.meta.to_json)
|
41
|
+
map_payment_captures_to_spree(order) if order.flow_io_payments.present?
|
44
42
|
order
|
45
43
|
else
|
46
44
|
errors << { message: "Order #{order_number} not found" }
|
@@ -48,21 +46,39 @@ module FlowcommerceSpree
|
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if (order_number =
|
58
|
-
if
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
49
|
+
def card_authorization_upserted_v2
|
50
|
+
card_auth = @data['authorization']&.to_hash
|
51
|
+
errors << { message: 'Authorization param missing' } && (return self) unless card_auth
|
52
|
+
|
53
|
+
errors << { message: 'Card param missing' } && (return self) unless (flow_io_card = card_auth.delete('card'))
|
54
|
+
|
55
|
+
if (order_number = card_auth.dig('order', 'number'))
|
56
|
+
if (order = Spree::Order.find_by(number: order_number))
|
57
|
+
flow_io_card_expiration = flow_io_card.delete('expiration')
|
58
|
+
|
59
|
+
card = Spree::CreditCard.find_or_initialize_by(month: flow_io_card_expiration['month'].to_s,
|
60
|
+
year: flow_io_card_expiration['year'].to_s,
|
61
|
+
cc_type: flow_io_card.delete('type'),
|
62
|
+
last_digits: flow_io_card.delete('last4'),
|
63
|
+
name: flow_io_card.delete('name'),
|
64
|
+
user_id: order.user&.id)
|
65
|
+
card.flow_data ||= {}
|
66
|
+
card.flow_data.merge!(flow_io_card.except('discriminator')) if card.new_record?
|
67
|
+
card_auth['method'].delete('images')
|
68
|
+
card.push_authorization(card_auth.except('discriminator'))
|
69
|
+
if card.new_record?
|
70
|
+
card.imported = true
|
71
|
+
card.save!
|
63
72
|
else
|
64
|
-
|
73
|
+
card.update_column(:meta, card.meta.to_json)
|
65
74
|
end
|
75
|
+
|
76
|
+
order.payments.where(response_code: card_auth['id'])
|
77
|
+
.update_all(source_id: card.id, source_type: 'Spree::CreditCard')
|
78
|
+
|
79
|
+
return card
|
80
|
+
else
|
81
|
+
errors << { message: "Order #{order_number} not found" }
|
66
82
|
end
|
67
83
|
else
|
68
84
|
errors << { message: 'Order number param missing' }
|
@@ -71,7 +87,27 @@ module FlowcommerceSpree
|
|
71
87
|
self
|
72
88
|
end
|
73
89
|
|
74
|
-
def
|
90
|
+
def experience_upserted_v2
|
91
|
+
experience = @data['experience']
|
92
|
+
Spree::Zones::Product.find_or_initialize_by(name: experience['key'].titleize).store_flow_io_data(experience)
|
93
|
+
end
|
94
|
+
|
95
|
+
def fraud_status_changed
|
96
|
+
order_number = @data.dig('order', 'number')
|
97
|
+
errors << { message: 'Order number param missing' } && (return self) unless order_number
|
98
|
+
|
99
|
+
order = Spree::Order.find_by(number: order_number)
|
100
|
+
errors << { message: "Order #{order_number} not found" } && (return self) unless order
|
101
|
+
|
102
|
+
if @data['status'] == 'declined'
|
103
|
+
order.update_columns(fraudulent: true)
|
104
|
+
order.cancel!
|
105
|
+
end
|
106
|
+
|
107
|
+
order
|
108
|
+
end
|
109
|
+
|
110
|
+
def local_item_upserted
|
75
111
|
errors << { message: 'Local item param missing' } && (return self) unless (local_item = @data['local_item'])
|
76
112
|
|
77
113
|
errors << { message: 'SKU param missing' } && (return self) unless (flow_sku = local_item.dig('item', 'number'))
|
@@ -91,94 +127,28 @@ module FlowcommerceSpree
|
|
91
127
|
self
|
92
128
|
end
|
93
129
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
130
|
+
def map_payment_captures_to_spree(order)
|
131
|
+
payments = order.flow_data&.dig('order', 'payments')
|
132
|
+
order.flow_data['captures']&.each do |c|
|
133
|
+
next unless c['status'] == 'succeeded'
|
98
134
|
|
99
|
-
|
135
|
+
auth = c.dig('authorization', 'id')
|
136
|
+
next unless payments&.find { |p| p['reference'] == auth }
|
100
137
|
|
101
|
-
|
102
|
-
order.flow_data['order'] = flow_order.to_hash
|
103
|
-
order.flow_data['allocations'] = flow_allocation.to_hash
|
104
|
-
order_flow_data = order.flow_data['order']
|
105
|
-
attrs_to_update = { meta: order.meta.to_json }
|
106
|
-
flow_data_submitted = order_flow_data['submitted_at'].present?
|
107
|
-
if flow_data_submitted && !order.complete?
|
108
|
-
if order_flow_data['payments'].present? && (order_flow_data.dig('balance', 'amount')&.to_i == 0)
|
109
|
-
attrs_to_update[:state] = 'complete'
|
110
|
-
attrs_to_update[:payment_state] = 'paid'
|
111
|
-
attrs_to_update[:completed_at] = Time.zone.now.utc
|
112
|
-
attrs_to_update[:email] = order.flow_customer_email
|
113
|
-
else
|
114
|
-
attrs_to_update[:state] = 'confirmed'
|
115
|
-
end
|
116
|
-
end
|
138
|
+
next unless (payment = Spree::Payment.find_by(response_code: auth))
|
117
139
|
|
118
|
-
|
140
|
+
next if Spree::PaymentCaptureEvent.where("meta -> 'flow_data' ->> 'id' = ?", c['id']).exists?
|
119
141
|
|
120
|
-
|
121
|
-
|
122
|
-
order.shipment.update_amounts
|
123
|
-
order.line_items.each(&:store_ets)
|
124
|
-
end
|
142
|
+
payment.capture_events.create!(amount: c['amount'], meta: { 'flow_data' => { 'id' => c['id'] } })
|
143
|
+
return if payment.completed? || payment.capture_events.sum(:amount) < payment.amount
|
125
144
|
|
126
|
-
|
127
|
-
|
128
|
-
# TODO: To be refactored once we have the capture_upserted_v2 webhook configured
|
129
|
-
if flow_data_submitted
|
130
|
-
order.create_tax_charge!
|
131
|
-
order.finalize!
|
132
|
-
order.update_totals
|
133
|
-
order.save
|
134
|
-
end
|
135
|
-
|
136
|
-
return order
|
137
|
-
else
|
138
|
-
errors << { message: "Order #{order_number} not found" }
|
145
|
+
payment.complete
|
139
146
|
end
|
140
147
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
def hook_order_upserted_v2
|
145
|
-
errors << { message: 'Order param missing' } && (return self) unless (flow_order = @data['order'])
|
146
|
-
|
147
|
-
errors << { message: 'Order number param missing' } && (return self) unless (order_number = flow_order['number'])
|
148
|
-
|
149
|
-
if (order = Spree::Order.find_by(number: order_number))
|
150
|
-
order.flow_data['order'] = flow_order.to_hash
|
151
|
-
order_flow_data = order.flow_data['order']
|
152
|
-
attrs_to_update = { meta: order.meta.to_json }
|
153
|
-
flow_data_submitted = order_flow_data['submitted_at'].present?
|
154
|
-
if flow_data_submitted && !order.complete?
|
155
|
-
if order_flow_data['payments'].present? && (order_flow_data.dig('balance', 'amount')&.to_i == 0)
|
156
|
-
attrs_to_update[:state] = 'complete'
|
157
|
-
attrs_to_update[:payment_state] = 'paid'
|
158
|
-
attrs_to_update[:completed_at] = Time.zone.now.utc
|
159
|
-
attrs_to_update[:email] = order.flow_customer_email
|
160
|
-
else
|
161
|
-
attrs_to_update[:state] = 'confirmed'
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
attrs_to_update.merge!(order.prepare_flow_addresses) if order.complete? || attrs_to_update[:state] == 'complete'
|
166
|
-
|
167
|
-
order.update_columns(attrs_to_update)
|
168
|
-
order.create_tax_charge! if flow_data_submitted
|
169
|
-
return order
|
170
|
-
else
|
171
|
-
errors << { message: "Order #{order_number} not found" }
|
172
|
-
end
|
173
|
-
|
174
|
-
self
|
175
|
-
end
|
176
|
-
|
177
|
-
# send en email when order is refunded
|
178
|
-
def hook_refund_upserted_v2
|
179
|
-
Spree::OrderMailer.refund_complete_email(@data).deliver
|
148
|
+
return if order.completed?
|
149
|
+
return unless order.flow_io_captures_sum >= order.flow_io_total_amount && order.flow_io_balance_amount <= 0
|
180
150
|
|
181
|
-
|
151
|
+
FlowcommerceSpree::OrderUpdater.new(order: order).finalize_order
|
182
152
|
end
|
183
153
|
end
|
184
154
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flowcommerce_spree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aurel Branzeanu
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-03-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colorize
|
@@ -101,20 +101,6 @@ dependencies:
|
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '0.21'
|
104
|
-
- !ruby/object:Gem::Dependency
|
105
|
-
name: request_store
|
106
|
-
requirement: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
type: :runtime
|
112
|
-
prerelease: false
|
113
|
-
version_requirements: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
104
|
- !ruby/object:Gem::Dependency
|
119
105
|
name: spree_backend
|
120
106
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,6 +145,8 @@ files:
|
|
159
145
|
- app/assets/javascripts/flowcommerce_spree/application.js
|
160
146
|
- app/assets/stylesheets/flowcommerce_spree/application.css
|
161
147
|
- app/controllers/concerns/current_zone_loader_decorator.rb
|
148
|
+
- app/controllers/flowcommerce_spree/inventory_controller.rb
|
149
|
+
- app/controllers/flowcommerce_spree/orders_controller.rb
|
162
150
|
- app/controllers/flowcommerce_spree/webhooks_controller.rb
|
163
151
|
- app/controllers/users/sessions_controller_decorator.rb
|
164
152
|
- app/helpers/flowcommerce_spree/application_helper.rb
|
@@ -169,12 +157,12 @@ files:
|
|
169
157
|
- app/models/spree/address_decorator.rb
|
170
158
|
- app/models/spree/calculator/flow_io.rb
|
171
159
|
- app/models/spree/calculator/shipping/flow_io.rb
|
172
|
-
- app/models/spree/
|
160
|
+
- app/models/spree/flow_io_credit_card_decorator.rb
|
161
|
+
- app/models/spree/flow_io_order_decorator.rb
|
173
162
|
- app/models/spree/flow_io_product_decorator.rb
|
174
163
|
- app/models/spree/flow_io_variant_decorator.rb
|
175
164
|
- app/models/spree/gateway/flow_io.rb
|
176
|
-
- app/models/spree/
|
177
|
-
- app/models/spree/order_decorator.rb
|
165
|
+
- app/models/spree/payment_capture_event_decorator.rb
|
178
166
|
- app/models/spree/promotion_decorator.rb
|
179
167
|
- app/models/spree/promotion_handler/coupon_decorator.rb
|
180
168
|
- app/models/spree/spree_user_decorator.rb
|
@@ -182,11 +170,14 @@ files:
|
|
182
170
|
- app/models/spree/zone_decorator.rb
|
183
171
|
- app/models/spree/zones/flow_io_product_zone_decorator.rb
|
184
172
|
- app/models/tracking/setup_decorator.rb
|
173
|
+
- app/serializers/api/v2/order_serializer_decorator.rb
|
185
174
|
- app/services/flowcommerce_spree/import_experience_items.rb
|
186
175
|
- app/services/flowcommerce_spree/import_experiences.rb
|
187
176
|
- app/services/flowcommerce_spree/order_sync.rb
|
177
|
+
- app/services/flowcommerce_spree/order_updater.rb
|
188
178
|
- app/views/layouts/flowcommerce_spree/application.html.erb
|
189
179
|
- app/views/spree/admin/payments/index.html.erb
|
180
|
+
- app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb
|
190
181
|
- app/views/spree/admin/promotions/edit.html.erb
|
191
182
|
- app/views/spree/admin/shared/_order_summary.html.erb
|
192
183
|
- app/views/spree/admin/shared/_order_summary_flow.html.erb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Flow (2017)
|
4
|
-
# Enable this modifications if you want to display flow localized line item
|
5
|
-
# Example: https://i.imgur.com/7v2ix2G.png
|
6
|
-
module Spree
|
7
|
-
LineItem.class_eval do
|
8
|
-
# admin show line item price
|
9
|
-
def single_money
|
10
|
-
price = display_price.to_s
|
11
|
-
price += " (#{order.flow_line_item_price(self)})" if order.flow_order
|
12
|
-
price
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|