flowcommerce_spree 0.0.2 → 0.0.7
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/README.md +46 -13
- data/SPREE_FLOW.md +6 -28
- data/app/controllers/concerns/current_zone_loader_decorator.rb +33 -25
- data/app/controllers/flowcommerce_spree/inventory_controller.rb +23 -0
- data/app/controllers/flowcommerce_spree/orders_controller.rb +20 -0
- data/app/controllers/flowcommerce_spree/webhooks_controller.rb +23 -13
- data/app/controllers/users/sessions_controller_decorator.rb +28 -0
- data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +4 -9
- data/app/models/spree/address_decorator.rb +19 -0
- data/app/models/spree/calculator/flow_io.rb +61 -0
- data/app/models/spree/calculator/shipping/flow_io.rb +40 -0
- data/app/models/spree/flow_io_credit_card_decorator.rb +21 -0
- data/app/models/spree/flow_io_order_decorator.rb +163 -0
- data/app/models/spree/flow_io_variant_decorator.rb +4 -2
- data/app/models/spree/gateway/flow_io.rb +153 -0
- data/app/models/spree/{credit_card_decorator.rb → payment_capture_event_decorator.rb} +1 -1
- data/app/models/spree/promotion_handler/coupon_decorator.rb +1 -1
- data/app/models/spree/zones/flow_io_product_zone_decorator.rb +8 -0
- data/app/models/tracking/setup_decorator.rb +40 -0
- data/app/overrides/spree/admin/order_sidebar_summary_flow_link.rb +13 -0
- data/app/overrides/spree/admin/products/order_price_flow_message.rb +9 -0
- 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 +81 -173
- data/app/services/flowcommerce_spree/order_updater.rb +78 -0
- data/app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb +76 -0
- data/app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb +66 -0
- data/app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb +25 -0
- data/app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb +35 -0
- data/app/services/flowcommerce_spree/webhooks/local_item_upserted.rb +40 -0
- data/app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb +21 -0
- data/config/rails_best_practices.yml +51 -0
- data/config/routes.rb +3 -1
- 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 +17 -3
- data/lib/flowcommerce_spree/engine.rb +33 -3
- data/lib/flowcommerce_spree/experience_service.rb +1 -27
- data/lib/flowcommerce_spree/logging_http_client.rb +33 -15
- data/lib/flowcommerce_spree/session.rb +17 -32
- data/lib/flowcommerce_spree/test_support.rb +7 -0
- data/lib/flowcommerce_spree/version.rb +1 -1
- data/lib/tasks/flowcommerce_spree.rake +4 -1
- metadata +88 -21
- data/app/mailers/spree/spree_order_mailer_decorator.rb +0 -24
- data/app/models/spree/gateway/spree_flow_gateway.rb +0 -116
- data/app/models/spree/line_item_decorator.rb +0 -15
- data/app/models/spree/order_decorator.rb +0 -179
- data/app/views/spree/order_mailer/confirm_email.html.erb +0 -86
- data/app/views/spree/order_mailer/confirm_email.text.erb +0 -38
- data/config/initializers/flowcommerce_spree.rb +0 -7
- data/lib/flow/error.rb +0 -73
- data/lib/flow/pay_pal.rb +0 -25
- data/lib/flowcommerce_spree/webhook_service.rb +0 -98
- data/lib/simple_csv_writer.rb +0 -44
@@ -0,0 +1,13 @@
|
|
1
|
+
Deface::Override.new(
|
2
|
+
virtual_path: 'spree/admin/shared/_order_tabs',
|
3
|
+
name: 'spree_admin_order_additional_information_flow_message',
|
4
|
+
insert_top: '.additional-info',
|
5
|
+
text: '
|
6
|
+
<% if FlowcommerceSpree::ORGANIZATION.present? && @order.flow_order.present? %>
|
7
|
+
<div style="text-align: center">
|
8
|
+
<%= link_to "See Flow Order",
|
9
|
+
"https://console.flow.io/#{FlowcommerceSpree::ORGANIZATION}/orders/#{@order.number}",
|
10
|
+
target: "_blank", class: "button" %>
|
11
|
+
</div>
|
12
|
+
<% end %>'
|
13
|
+
)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Deface::Override.new(
|
2
|
+
virtual_path: 'spree/admin/prices/index',
|
3
|
+
name: 'spree_admin_prices_flow_mesage',
|
4
|
+
insert_top: '.no-border-top',
|
5
|
+
text: "
|
6
|
+
<div class='spree-admin-info' >
|
7
|
+
To check localized pricing, please click <a href='#{"https://console.flow.io/#{ENV['FLOW_ORGANIZATION']}/price-books"}' target='_blank'>here</a>.
|
8
|
+
</div>"
|
9
|
+
)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Api::V2::OrderSerializer.class_eval do
|
4
|
+
attribute :duty_included, if: proc { object.flow_io_attributes.present? }
|
5
|
+
attribute :vat_included, if: proc { object.flow_io_attributes.present? }
|
6
|
+
|
7
|
+
def duty_included
|
8
|
+
flow_io_order_attributes&.[]('duty') == 'included'
|
9
|
+
end
|
10
|
+
|
11
|
+
def vat_included
|
12
|
+
flow_io_order_attributes&.[]('vat') == 'included'
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def flow_io_order_attributes
|
18
|
+
@flow_io_order_attributes ||= Oj.load(object.flow_io_attributes['pricing_key'])
|
19
|
+
end
|
20
|
+
end
|
@@ -13,7 +13,7 @@ module FlowcommerceSpree
|
|
13
13
|
items = []
|
14
14
|
total = 0
|
15
15
|
|
16
|
-
while offset == 0 || items.length
|
16
|
+
while offset == 0 || items.length != 0
|
17
17
|
# show current list size
|
18
18
|
@logger.info "\nGetting items: #{@experience_key.green}, rows #{offset} - #{offset + page_size}"
|
19
19
|
|
@@ -2,196 +2,118 @@
|
|
2
2
|
|
3
3
|
module FlowcommerceSpree
|
4
4
|
# represents flow.io order syncing service
|
5
|
-
# for easy integration we are currently passing:
|
6
|
-
# - flow experience
|
7
|
-
# - spree order
|
8
|
-
# - current customer, present as @current_spree_user controller instance variable
|
9
|
-
#
|
10
|
-
# example:
|
11
|
-
# flow_order = FlowcommerceSpree::OrderSync.new # init flow-order object
|
12
|
-
# order: Spree::Order.last,
|
13
|
-
# experience: @flow_session.experience
|
14
|
-
# customer: Spree::User.last
|
15
|
-
# flow_order.build_flow_request # builds json body to be posted to flow.io api
|
16
|
-
# flow_order.synchronize! # sends order to flow
|
17
5
|
class OrderSync
|
18
6
|
FLOW_CENTER = 'default'
|
19
7
|
|
20
|
-
attr_reader :
|
8
|
+
attr_reader :order, :response
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
# @param [Object] order
|
11
|
+
# @param [String] flow_session_id
|
12
|
+
def initialize(order:, flow_session_id:)
|
13
|
+
raise(ArgumentError, 'Experience not defined or not active') unless order&.zone&.flow_io_active_experience?
|
25
14
|
|
26
|
-
order.flow_data.delete('order')
|
27
|
-
order.update_column :meta, order.meta.to_json
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def initialize(order:)
|
32
|
-
raise(ArgumentError, 'Experience not defined or not active') unless order.zone&.flow_io_active_experience?
|
33
|
-
|
34
|
-
@client = FlowcommerceSpree.client(session_id: order.flow_data['session_id'])
|
35
15
|
@experience = order.flow_io_experience_key
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
16
|
+
@flow_session_id = flow_session_id
|
17
|
+
@order = order
|
18
|
+
@client = FlowcommerceSpree.client(default_headers: { "Authorization": "Session #{flow_session_id}" },
|
19
|
+
authorization: nil)
|
39
20
|
end
|
40
21
|
|
41
22
|
# helper method to send complete order from Spree to flow.io
|
42
23
|
def synchronize!
|
43
|
-
|
44
|
-
check_state!
|
45
|
-
write_response_in_cache
|
46
|
-
@response
|
47
|
-
end
|
24
|
+
return unless @order.state == 'cart' && @order.line_items.size > 0
|
48
25
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
26
|
+
sync_body!
|
27
|
+
write_response_to_order
|
52
28
|
|
53
|
-
|
54
|
-
|
29
|
+
@order.update_columns(total: @order.total, meta: @order.meta.to_json)
|
30
|
+
refresh_checkout_token
|
55
31
|
end
|
56
32
|
|
57
33
|
def error?
|
58
34
|
@response&.[]('code') && @response&.[]('messages') ? true : false
|
59
35
|
end
|
60
36
|
|
61
|
-
|
62
|
-
deliveries.select { |el| el[:active] }.first
|
63
|
-
end
|
64
|
-
|
65
|
-
# delivery methods are defined in flow console
|
66
|
-
def deliveries
|
67
|
-
# if we have erorr with an order, but still using this method
|
68
|
-
return [] unless @order.flow_order
|
69
|
-
|
70
|
-
@order.flow_data ||= {}
|
71
|
-
|
72
|
-
delivery_list = @order.flow_order['deliveries'][0]['options']
|
73
|
-
delivery_list = delivery_list.map do |opts|
|
74
|
-
name = opts['tier']['name']
|
75
|
-
|
76
|
-
# add original Flow ID
|
77
|
-
# name += ' (%s)' % opts['tier']['strategy'] if opts['tier']['strategy']
|
78
|
-
|
79
|
-
selection_id = opts['id']
|
80
|
-
|
81
|
-
{ id: selection_id,
|
82
|
-
price: { label: opts['price']['label'] },
|
83
|
-
active: @order.flow_order['selections'].include?(selection_id),
|
84
|
-
name: name }
|
85
|
-
end.to_a
|
86
|
-
|
87
|
-
# make first one active unless we have active element
|
88
|
-
delivery_list.first[:active] = true unless delivery_list.select { |el| el[:active] }.first
|
89
|
-
|
90
|
-
delivery_list
|
91
|
-
end
|
92
|
-
|
93
|
-
def total_price
|
94
|
-
@order.flow_total
|
95
|
-
end
|
96
|
-
|
97
|
-
def delivered_duty
|
98
|
-
# paid is default
|
99
|
-
@order.flow_data['delivered_duty'] || ::Io::Flow::V0::Models::DeliveredDuty.paid.value
|
100
|
-
end
|
37
|
+
private
|
101
38
|
|
102
39
|
# builds object that can be sent to api.flow.io to sync order data
|
103
40
|
def build_flow_request
|
104
|
-
@
|
41
|
+
@opts = { experience: @experience, expand: ['experience'] }
|
42
|
+
@body = { items: @order.line_items.map { |line_item| add_item(line_item) } }
|
105
43
|
|
106
|
-
|
107
|
-
@opts[:experience] = @experience
|
108
|
-
@opts[:expand] = ['experience']
|
44
|
+
try_to_add_customer
|
109
45
|
|
110
|
-
|
46
|
+
return unless (flow_data = @order.flow_data['order'])
|
111
47
|
|
112
|
-
|
48
|
+
@body[:selections] = flow_data['selections'].presence
|
49
|
+
@body[:delivered_duty] = flow_data['delivered_duty'].presence
|
50
|
+
@body[:attributes] = flow_data['attributes'].presence
|
113
51
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@body[:attributes] = flow_data['attributes'].presence
|
52
|
+
# discount on full order is applied
|
53
|
+
@body[:discount] = { amount: @order.adjustment_total, currency: @order.currency } if @order.adjustment_total != 0
|
54
|
+
end
|
118
55
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
56
|
+
def refresh_checkout_token
|
57
|
+
root_url = Rails.application.routes.url_helpers.root_url
|
58
|
+
order_number = @order.number
|
59
|
+
confirmation_url = "#{root_url}flow/order-completed?order=#{order_number}&t=#{@order.guest_token}"
|
60
|
+
@order.flow_io_attribute_add('flow_return_url', confirmation_url)
|
61
|
+
@order.flow_io_attribute_add('checkout_continue_shopping_url', root_url)
|
124
62
|
|
125
|
-
|
126
|
-
|
63
|
+
FlowcommerceSpree.client.checkout_tokens.post_checkout_and_tokens_by_organization(
|
64
|
+
FlowcommerceSpree::ORGANIZATION, discriminator: 'checkout_token_reference_form',
|
65
|
+
order_number: order_number,
|
66
|
+
session_id: @flow_session_id,
|
67
|
+
urls: { continue_shopping: root_url,
|
68
|
+
confirmation: confirmation_url,
|
69
|
+
invalid_checkout: root_url }
|
70
|
+
)&.id
|
127
71
|
end
|
128
72
|
|
129
|
-
private
|
130
|
-
|
131
73
|
# if customer is defined, add customer info
|
132
74
|
# it is possible to have order in Spree without customer info (new guest session)
|
133
|
-
def
|
134
|
-
return unless @
|
135
|
-
|
136
|
-
address = @customer.ship_address
|
137
|
-
# address = nil
|
138
|
-
if address
|
139
|
-
@body[:customer] = { name: { first: address.firstname,
|
140
|
-
last: address.lastname },
|
141
|
-
email: @customer.email,
|
142
|
-
number: @customer.flow_number,
|
143
|
-
phone: address.phone }
|
144
|
-
|
145
|
-
streets = []
|
146
|
-
streets.push address.address1 unless address.address1.blank?
|
147
|
-
streets.push address.address2 unless address.address2.blank?
|
148
|
-
|
149
|
-
@body[:destination] = { streets: streets,
|
150
|
-
city: address.city,
|
151
|
-
province: address.state_name,
|
152
|
-
postal: address.zipcode,
|
153
|
-
country: (address.country.iso3 || 'USA'),
|
154
|
-
contact: @body[:customer] }
|
155
|
-
|
156
|
-
@body[:destination].delete_if { |_k, v| v.nil? }
|
157
|
-
end
|
75
|
+
def try_to_add_customer
|
76
|
+
return unless (customer = @order.user)
|
158
77
|
|
159
|
-
|
160
|
-
|
78
|
+
address = nil
|
79
|
+
customer_ship_address = customer.ship_address
|
80
|
+
address = customer_ship_address if customer_ship_address&.country&.iso3 == @order.zone.flow_io_experience_country
|
161
81
|
|
162
|
-
|
163
|
-
|
82
|
+
customer_profile = customer.user_profile
|
83
|
+
unless address
|
84
|
+
user_profile_address = customer_profile&.address
|
85
|
+
address = user_profile_address if user_profile_address&.country&.iso3 == @order.zone.flow_io_experience_country
|
86
|
+
end
|
164
87
|
|
165
|
-
@
|
88
|
+
@body[:customer] = { name: { first: address&.firstname || customer_profile&.first_name,
|
89
|
+
last: address&.lastname || customer_profile&.last_name },
|
90
|
+
email: customer.email,
|
91
|
+
number: customer.flow_number,
|
92
|
+
phone: address&.phone }
|
166
93
|
|
167
|
-
|
168
|
-
|
94
|
+
add_customer_address(address) if address
|
95
|
+
end
|
169
96
|
|
170
|
-
|
171
|
-
|
97
|
+
def add_customer_address(address)
|
98
|
+
streets = []
|
99
|
+
streets.push address.address1 if address.address1.present?
|
100
|
+
streets.push address.address2 if address.address2.present?
|
172
101
|
|
173
|
-
|
174
|
-
|
102
|
+
@body[:destination] = { streets: streets,
|
103
|
+
city: address.city,
|
104
|
+
province: address.state_name,
|
105
|
+
postal: address.zipcode,
|
106
|
+
country: (address.country&.iso3 || ''),
|
107
|
+
contact: @body[:customer] }
|
175
108
|
|
176
|
-
|
177
|
-
@response ||= FlowcommerceSpree::Api.run :get, "/:organization/orders/#{@body[:number]}", expand: 'experience'
|
178
|
-
else
|
179
|
-
@response = @client.orders.put_by_number(FlowcommerceSpree::ORGANIZATION, @order.number,
|
180
|
-
Io::Flow::V0::Models::OrderPutForm.new(@body), @opts).to_hash
|
181
|
-
end
|
109
|
+
@body[:destination].delete_if { |_k, v| v.nil? }
|
182
110
|
end
|
183
111
|
|
184
|
-
def
|
185
|
-
|
186
|
-
# if !@order.flow_order_authorized?
|
187
|
-
|
188
|
-
# authorize payment on complete, unless authorized
|
189
|
-
if @order.state == 'complete' && !@order.flow_order_authorized?
|
190
|
-
simple_gateway = Flow::SimpleGateway.new(@order)
|
191
|
-
simple_gateway.cc_authorization
|
192
|
-
end
|
112
|
+
def sync_body!
|
113
|
+
build_flow_request
|
193
114
|
|
194
|
-
@
|
115
|
+
@response = @client.orders.put_by_number(ORGANIZATION, @order.number,
|
116
|
+
Io::Flow::V0::Models::OrderPutForm.new(@body), @opts).to_hash
|
195
117
|
end
|
196
118
|
|
197
119
|
def add_item(line_item)
|
@@ -199,33 +121,19 @@ module FlowcommerceSpree
|
|
199
121
|
price_root = variant.flow_data&.dig('exp', @experience, 'prices')&.[](0) || {}
|
200
122
|
|
201
123
|
# create flow order line item
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
@items.push item
|
124
|
+
{ center: FLOW_CENTER,
|
125
|
+
number: variant.sku,
|
126
|
+
quantity: line_item.quantity,
|
127
|
+
price: { amount: price_root['amount'] || variant.cost_price,
|
128
|
+
currency: price_root['currency'] || variant.cost_currency } }
|
209
129
|
end
|
210
130
|
|
211
|
-
|
212
|
-
|
213
|
-
def write_response_in_cache
|
214
|
-
if !@response || error?
|
215
|
-
@order.flow_data.delete('digest')
|
216
|
-
@order.flow_data.delete('order')
|
217
|
-
else
|
218
|
-
response_total = @response.dig('total', 'label')
|
219
|
-
cache_total = @order.flow_data.dig('order', 'total', 'label')
|
220
|
-
|
221
|
-
# return if total is not changed, no products removed or added
|
222
|
-
return if @use_get && response_total == cache_total
|
223
|
-
|
224
|
-
# update local order
|
225
|
-
@order.flow_data.merge!('digest' => @digest, 'order' => @response.to_hash)
|
226
|
-
end
|
131
|
+
def write_response_to_order
|
132
|
+
return @order.flow_data.delete('order') if !@response || error?
|
227
133
|
|
228
|
-
|
134
|
+
# update local order
|
135
|
+
@order.total = @response[:total]&.[](:amount)
|
136
|
+
@order.flow_data.merge!('order' => @response)
|
229
137
|
end
|
230
138
|
end
|
231
139
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowcommerceSpree
|
4
|
+
class OrderUpdater
|
5
|
+
def initialize(order:)
|
6
|
+
unless order&.zone&.flow_io_active_or_archiving_experience?
|
7
|
+
raise(ArgumentError, 'Experience not defined or not active')
|
8
|
+
end
|
9
|
+
|
10
|
+
@experience = order.flow_io_experience_key
|
11
|
+
@order = order
|
12
|
+
@client = FlowcommerceSpree.client
|
13
|
+
end
|
14
|
+
|
15
|
+
def upsert_data(flow_io_order = nil)
|
16
|
+
return if @order.state == 'complete'
|
17
|
+
|
18
|
+
flow_io_order ||= @client.orders.get_by_number(FlowcommerceSpree::ORGANIZATION, @order.number).to_hash
|
19
|
+
|
20
|
+
@order.flow_data['order'] = flow_io_order
|
21
|
+
return if @order.flow_data.dig('order', 'submitted_at').blank?
|
22
|
+
|
23
|
+
attrs_to_update = { meta: @order.meta.to_json, email: @order.flow_customer_email, payment_state: 'pending' }
|
24
|
+
attrs_to_update.merge!(@order.prepare_flow_addresses)
|
25
|
+
@order.update_columns(attrs_to_update)
|
26
|
+
@order.state = 'delivery'
|
27
|
+
@order.save!
|
28
|
+
@order.create_proposed_shipments
|
29
|
+
@order.shipment.update_amounts
|
30
|
+
@order.line_items.each(&:store_ets)
|
31
|
+
@order.charge_taxes
|
32
|
+
|
33
|
+
@order.state = 'payment'
|
34
|
+
@order.save!
|
35
|
+
end
|
36
|
+
|
37
|
+
def finalize_order
|
38
|
+
@order.reload
|
39
|
+
@order.finalize!
|
40
|
+
@order.update_totals
|
41
|
+
@order.save
|
42
|
+
@order.after_completed_order
|
43
|
+
end
|
44
|
+
|
45
|
+
def complete_checkout
|
46
|
+
upsert_data
|
47
|
+
map_payments_to_spree
|
48
|
+
finalize_order if @order.state == 'complete'
|
49
|
+
end
|
50
|
+
|
51
|
+
def map_payments_to_spree
|
52
|
+
@order.flow_io_payments&.each do |p|
|
53
|
+
payment =
|
54
|
+
@order.payments.find_or_initialize_by(response_code: p['reference'], payment_method_id: payment_method_id)
|
55
|
+
next unless payment.new_record?
|
56
|
+
|
57
|
+
payment.amount = p.dig('total', 'amount')
|
58
|
+
payment.pend
|
59
|
+
|
60
|
+
# For now this additional update is overwriting the generated identifier with flow.io payment identifier.
|
61
|
+
# TODO: Check and possibly refactor in Spree 3.0, where the `before_create :set_unique_identifier`
|
62
|
+
# has been removed.
|
63
|
+
payment.update_column(:identifier, p['id'])
|
64
|
+
end
|
65
|
+
|
66
|
+
return if @order.payments.sum(:amount) < @order.amount || @order.state == 'complete'
|
67
|
+
|
68
|
+
@order.state = 'confirm'
|
69
|
+
@order.save!
|
70
|
+
@order.state = 'complete'
|
71
|
+
@order.save!
|
72
|
+
end
|
73
|
+
|
74
|
+
def payment_method_id
|
75
|
+
@payment_method_id ||= Spree::PaymentMethod.find_by(active: true, type: 'Spree::Gateway::FlowIo').id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowcommerceSpree
|
4
|
+
module Webhooks
|
5
|
+
class CaptureUpsertedV2
|
6
|
+
attr_reader :errors
|
7
|
+
alias full_messages errors
|
8
|
+
|
9
|
+
def self.process(data)
|
10
|
+
new(data).process
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(data)
|
14
|
+
@data = data
|
15
|
+
@errors = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
errors << { message: 'Capture param missing' } && (return self) unless (capture = @data['capture']&.to_hash)
|
20
|
+
|
21
|
+
order_number = capture.dig('authorization', 'order', 'number')
|
22
|
+
errors << { message: 'Order number param missing' } && (return self) unless order_number
|
23
|
+
|
24
|
+
if (order = Spree::Order.find_by(number: order_number))
|
25
|
+
upsert_order_captures(order, capture)
|
26
|
+
payments = order.flow_io_payments
|
27
|
+
map_payment_captures_to_spree(order, payments) if payments.present?
|
28
|
+
order
|
29
|
+
else
|
30
|
+
errors << { message: "Order #{order_number} not found" }
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def upsert_order_captures(order, capture)
|
38
|
+
order.flow_data ||= {}
|
39
|
+
order.flow_data['captures'] ||= []
|
40
|
+
order_captures = order.flow_data['captures']
|
41
|
+
order_captures.delete_if { |c| c['id'] == capture['id'] }
|
42
|
+
order_captures << capture
|
43
|
+
order.update_column(:meta, order.meta.to_json)
|
44
|
+
end
|
45
|
+
|
46
|
+
def map_payment_captures_to_spree(order, payments)
|
47
|
+
order.flow_data['captures']&.each do |c|
|
48
|
+
next unless (payment = captured_payment(payments, c))
|
49
|
+
|
50
|
+
payment.capture_events.create!(amount: c['amount'], meta: { 'flow_data' => { 'id' => c['id'] } })
|
51
|
+
return if payment.completed? || payment.capture_events.sum(:amount) < payment.amount
|
52
|
+
|
53
|
+
payment.complete
|
54
|
+
end
|
55
|
+
|
56
|
+
return if order.completed?
|
57
|
+
return unless order.flow_io_captures_sum >= order.flow_io_total_amount && order.flow_io_balance_amount <= 0
|
58
|
+
|
59
|
+
FlowcommerceSpree::OrderUpdater.new(order: order).finalize_order
|
60
|
+
end
|
61
|
+
|
62
|
+
def captured_payment(flow_order_payments, capture)
|
63
|
+
return unless capture['status'] == 'succeeded'
|
64
|
+
|
65
|
+
auth = capture.dig('authorization', 'id')
|
66
|
+
return unless flow_order_payments&.find { |p| p['reference'] == auth }
|
67
|
+
|
68
|
+
return unless (payment = Spree::Payment.find_by(response_code: auth))
|
69
|
+
|
70
|
+
return if Spree::PaymentCaptureEvent.where("meta -> 'flow_data' ->> 'id' = ?", capture['id']).exists?
|
71
|
+
|
72
|
+
payment
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|