flowcommerce_spree 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -6
  3. data/app/controllers/flowcommerce_spree/webhooks_controller.rb +16 -18
  4. data/app/models/spree/app_configuration_decorator.rb +7 -0
  5. data/app/models/spree/calculator/shipping/flow_io.rb +5 -2
  6. data/app/models/spree/flow_io_order_decorator.rb +11 -58
  7. data/app/models/spree/flow_io_variant_decorator.rb +2 -0
  8. data/app/overrides/spree/admin/order_sidebar_summary_flow_link.rb +13 -0
  9. data/app/overrides/spree/admin/products/order_price_flow_message.rb +9 -0
  10. data/app/services/flowcommerce_spree/order_sync.rb +38 -81
  11. data/app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb +76 -0
  12. data/app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb +66 -0
  13. data/app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb +25 -0
  14. data/app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb +35 -0
  15. data/app/services/flowcommerce_spree/webhooks/local_item_upserted.rb +40 -0
  16. data/config/routes.rb +1 -1
  17. data/lib/flowcommerce_spree.rb +3 -1
  18. data/lib/flowcommerce_spree/engine.rb +5 -0
  19. data/lib/flowcommerce_spree/experience_service.rb +1 -27
  20. data/lib/flowcommerce_spree/version.rb +1 -1
  21. data/lib/tasks/flowcommerce_spree.rake +4 -1
  22. metadata +74 -17
  23. data/app/mailers/spree/spree_order_mailer_decorator.rb +0 -24
  24. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -86
  25. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -38
  26. data/lib/flow/error.rb +0 -73
  27. data/lib/flow/pay_pal.rb +0 -25
  28. data/lib/flowcommerce_spree/webhook_service.rb +0 -154
  29. data/lib/simple_csv_writer.rb +0 -44
@@ -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
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class CardAuthorizationUpsertedV2
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['authorization']&.to_hash
15
+ @data&.[]('method')&.delete('images')
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ errors << { message: 'Authorization param missing' } && (return self) unless @data
21
+
22
+ errors << { message: 'Card param missing' } && (return self) unless (flow_io_card = @data.delete('card'))
23
+
24
+ if (order_number = @data.dig('order', 'number'))
25
+ if (order = Spree::Order.find_by(number: order_number))
26
+ card = upsert_card(flow_io_card, order)
27
+
28
+ order.payments.where(response_code: @data['id'])
29
+ .update_all(source_id: card.id, source_type: 'Spree::CreditCard')
30
+
31
+ return card
32
+ else
33
+ errors << { message: "Order #{order_number} not found" }
34
+ end
35
+ else
36
+ errors << { message: 'Order number param missing' }
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def upsert_card(flow_io_card, order)
45
+ flow_io_card_expiration = flow_io_card.delete('expiration')
46
+
47
+ card = Spree::CreditCard.find_or_initialize_by(month: flow_io_card_expiration['month'].to_s,
48
+ year: flow_io_card_expiration['year'].to_s,
49
+ cc_type: flow_io_card.delete('type'),
50
+ last_digits: flow_io_card.delete('last4'),
51
+ name: flow_io_card.delete('name'),
52
+ user_id: order.user&.id)
53
+ card.flow_data ||= {}
54
+ if card.new_record?
55
+ card.flow_data.merge!(flow_io_card.except('discriminator'))
56
+ card.imported = true
57
+ end
58
+
59
+ card.push_authorization(@data.except('discriminator'))
60
+ card.new_record? ? card.save : card.update_column(:meta, card.meta.to_json)
61
+
62
+ card
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class ExperienceUpsertedV2
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ experience = @data['experience']
21
+ Spree::Zones::Product.find_or_initialize_by(name: experience['key'].titleize).store_flow_io_data(experience)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class FraudStatusChanged
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ order_number = @data.dig('order', 'number')
21
+ errors << { message: 'Order number param missing' } && (return self) unless order_number
22
+
23
+ order = Spree::Order.find_by(number: order_number)
24
+ errors << { message: "Order #{order_number} not found" } && (return self) unless order
25
+
26
+ if @data['status'] == 'declined'
27
+ order.update_columns(fraudulent: true)
28
+ order.cancel!
29
+ end
30
+
31
+ order
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class LocalItemUpserted
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ errors << { message: 'Local item param missing' } && (return self) unless (local_item = @data['local_item'])
21
+
22
+ errors << { message: 'SKU param missing' } && (return self) unless (flow_sku = local_item.dig('item', 'number'))
23
+
24
+ if (variant = Spree::Variant.find_by(sku: flow_sku))
25
+ variant.add_flow_io_experience_data(
26
+ local_item.dig('experience', 'key'),
27
+ 'prices' => [local_item.dig('pricing', 'price')], 'status' => local_item['status']
28
+ )
29
+
30
+ variant.update_column(:meta, variant.meta.to_json)
31
+ return variant
32
+ else
33
+ errors << { message: "Variant with sku [#{flow_sku}] not found!" }
34
+ end
35
+
36
+ self
37
+ end
38
+ end
39
+ end
40
+ end
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  FlowcommerceSpree::Engine.routes.draw do
4
- post '/event-target', to: 'webhooks#handle_flow_web_hook_event'
4
+ post '/event-target', to: 'webhooks#handle_flow_io_event'
5
5
  get '/order-completed', to: 'orders#order_completed'
6
6
  post '/online-stock-availability', to: 'inventory#online_stock_availability'
7
7
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'activerecord-postgres-json'
4
+ require 'active_model_serializers'
3
5
  require 'flowcommerce'
4
6
  require 'flowcommerce_spree/api'
5
7
  require 'flowcommerce_spree/refresher'
6
8
  require 'flowcommerce_spree/engine'
7
9
  require 'flowcommerce_spree/logging_http_client'
8
10
  require 'flowcommerce_spree/logging_http_handler'
9
- require 'flowcommerce_spree/webhook_service'
10
11
  require 'flowcommerce_spree/session'
11
12
  require 'flow/simple_gateway'
13
+ require 'oj'
12
14
 
13
15
  module FlowcommerceSpree
14
16
  API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
@@ -9,6 +9,8 @@ module FlowcommerceSpree
9
9
  FlowcommerceSpree::ORGANIZATION = ENV.fetch('FLOW_ORGANIZATION', 'flow.io')
10
10
  FlowcommerceSpree::BASE_COUNTRY = ENV.fetch('FLOW_BASE_COUNTRY', 'USA')
11
11
  FlowcommerceSpree::API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
12
+ FlowcommerceSpree::FLOW_IO_WEBHOOK_USER = ENV.fetch('FLOW_IO_WEBHOOK_USER', 'test_user')
13
+ FlowcommerceSpree::FLOW_IO_WEBHOOK_PASSWORD = ENV.fetch('FLOW_IO_WEBHOOK_PASSWORD', 'test_password')
12
14
 
13
15
  FlowcommerceSpree::Config = FlowcommerceSpree::Settings.new
14
16
  end
@@ -40,6 +42,9 @@ module FlowcommerceSpree
40
42
  Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')).sort.each do |c|
41
43
  Rails.configuration.cache_classes ? require(c) : load(c)
42
44
  end
45
+ Dir.glob(File.join(File.dirname(__FILE__), '../app/overrides/*.rb')).sort.each do |c|
46
+ Rails.configuration.cache_classes ? require(c) : load(c)
47
+ end
43
48
  end
44
49
 
45
50
  config.to_prepare(&method(:activate).to_proc)
@@ -22,18 +22,6 @@ module FlowcommerceSpree
22
22
  nil
23
23
  end
24
24
 
25
- def get_by_subcatalog_id(subcatalog_id)
26
- fetch_from_flow.each do |experince|
27
- return experince if experince.subcatalog.id == subcatalog_id
28
- end
29
-
30
- nil
31
- end
32
-
33
- def compact
34
- all.map { |exp| [exp.country, exp.key, exp.name] }
35
- end
36
-
37
25
  def default
38
26
  FlowcommerceSpree::ExperienceService
39
27
  .all.select { |exp| exp.key.downcase == ENV.fetch('FLOW_BASE_COUNTRY').downcase }.first
@@ -42,24 +30,10 @@ module FlowcommerceSpree
42
30
  private
43
31
 
44
32
  def fetch_from_flow
45
- # return cached_experinces if cache_valid?
46
-
47
- experiences = FlowcommerceSpree.client.experiences.get ORGANIZATION
33
+ FlowcommerceSpree.client.experiences.get ORGANIZATION
48
34
 
49
35
  # work with active axperiences only
50
36
  # experiences = experiences.select { |it| it.status.value == 'active' }
51
-
52
- # @cache = [experiences, Time.now]
53
- experiences
54
- end
55
-
56
- def cache_valid?
57
- # cache experinces in worker memory for 1 minute
58
- @cache && @cache[1] > Time.now.ago(1.minute)
59
- end
60
-
61
- def cached_experinces
62
- @cache[0]
63
37
  end
64
38
  end
65
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FlowcommerceSpree
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
@@ -47,8 +47,11 @@ namespace :flowcommerce_spree do
47
47
 
48
48
  while current_page == 0 || !variants.empty?
49
49
  current_page += 1
50
- variants = Spree::Variant.order('updated_at desc').page(current_page).per(100).all
51
50
 
51
+ products = Spree::Product.where.not(country_of_origin: nil)
52
+ .order('updated_at desc')
53
+ .page(current_page).per(50).includes(:variants_including_master)
54
+ variants = products.map(&:variants_including_master).flatten
52
55
  variants.each do |variant|
53
56
  total_sum += 1
54
57
  result = variant.sync_product_to_flow
metadata CHANGED
@@ -1,16 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flowcommerce_spree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aurel Branzeanu
8
8
  - Sebastian De Luca
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-03-16 00:00:00.000000000 Z
12
+ date: 2021-04-07 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_model_serializers
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '='
19
+ - !ruby/object:Gem::Version
20
+ version: 0.10.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '='
26
+ - !ruby/object:Gem::Version
27
+ version: 0.10.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: activerecord-postgres-json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.2.3
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.2.3
14
42
  - !ruby/object:Gem::Dependency
15
43
  name: colorize
16
44
  requirement: !ruby/object:Gem::Requirement
@@ -77,16 +105,16 @@ dependencies:
77
105
  name: oj
78
106
  requirement: !ruby/object:Gem::Requirement
79
107
  requirements:
80
- - - ">="
108
+ - - '='
81
109
  - !ruby/object:Gem::Version
82
- version: '0'
110
+ version: 3.7.12
83
111
  type: :runtime
84
112
  prerelease: false
85
113
  version_requirements: !ruby/object:Gem::Requirement
86
114
  requirements:
87
- - - ">="
115
+ - - '='
88
116
  - !ruby/object:Gem::Version
89
- version: '0'
117
+ version: 3.7.12
90
118
  - !ruby/object:Gem::Dependency
91
119
  name: pg
92
120
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +143,20 @@ dependencies:
115
143
  - - "~>"
116
144
  - !ruby/object:Gem::Version
117
145
  version: 2.3.0
146
+ - !ruby/object:Gem::Dependency
147
+ name: factory_bot_rails
148
+ requirement: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '4.0'
153
+ type: :development
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '4.0'
118
160
  - !ruby/object:Gem::Dependency
119
161
  name: github_changelog_generator
120
162
  requirement: !ruby/object:Gem::Requirement
@@ -129,6 +171,20 @@ dependencies:
129
171
  - - ">="
130
172
  - !ruby/object:Gem::Version
131
173
  version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: rspec-rails
176
+ requirement: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '3.5'
181
+ type: :development
182
+ prerelease: false
183
+ version_requirements: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '3.5'
132
188
  description: Integration of popular Rails/Spree store framework with e-commerce Flow
133
189
  API
134
190
  email:
@@ -152,9 +208,9 @@ files:
152
208
  - app/helpers/flowcommerce_spree/application_helper.rb
153
209
  - app/helpers/spree/admin/orders_helper_decorator.rb
154
210
  - app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb
155
- - app/mailers/spree/spree_order_mailer_decorator.rb
156
211
  - app/models/flowcommerce_spree/settings.rb
157
212
  - app/models/spree/address_decorator.rb
213
+ - app/models/spree/app_configuration_decorator.rb
158
214
  - app/models/spree/calculator/flow_io.rb
159
215
  - app/models/spree/calculator/shipping/flow_io.rb
160
216
  - app/models/spree/flow_io_credit_card_decorator.rb
@@ -170,27 +226,30 @@ files:
170
226
  - app/models/spree/zone_decorator.rb
171
227
  - app/models/spree/zones/flow_io_product_zone_decorator.rb
172
228
  - app/models/tracking/setup_decorator.rb
229
+ - app/overrides/spree/admin/order_sidebar_summary_flow_link.rb
230
+ - app/overrides/spree/admin/products/order_price_flow_message.rb
173
231
  - app/serializers/api/v2/order_serializer_decorator.rb
174
232
  - app/services/flowcommerce_spree/import_experience_items.rb
175
233
  - app/services/flowcommerce_spree/import_experiences.rb
176
234
  - app/services/flowcommerce_spree/order_sync.rb
177
235
  - app/services/flowcommerce_spree/order_updater.rb
236
+ - app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb
237
+ - app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb
238
+ - app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb
239
+ - app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb
240
+ - app/services/flowcommerce_spree/webhooks/local_item_upserted.rb
178
241
  - app/views/layouts/flowcommerce_spree/application.html.erb
179
242
  - app/views/spree/admin/payments/index.html.erb
180
243
  - app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb
181
244
  - app/views/spree/admin/promotions/edit.html.erb
182
245
  - app/views/spree/admin/shared/_order_summary.html.erb
183
246
  - app/views/spree/admin/shared/_order_summary_flow.html.erb
184
- - app/views/spree/order_mailer/confirm_email.html.erb
185
- - app/views/spree/order_mailer/confirm_email.text.erb
186
247
  - config/rails_best_practices.yml
187
248
  - config/routes.rb
188
249
  - db/migrate/20201021160159_add_type_and_meta_to_spree_zone.rb
189
250
  - db/migrate/20201021755957_add_meta_to_spree_tables.rb
190
251
  - db/migrate/20201022173210_add_zone_type_to_spree_zone_members.rb
191
252
  - db/migrate/20201022174252_add_kind_to_zone.rb
192
- - lib/flow/error.rb
193
- - lib/flow/pay_pal.rb
194
253
  - lib/flow/simple_gateway.rb
195
254
  - lib/flowcommerce_spree.rb
196
255
  - lib/flowcommerce_spree/api.rb
@@ -202,14 +261,12 @@ files:
202
261
  - lib/flowcommerce_spree/session.rb
203
262
  - lib/flowcommerce_spree/test_support.rb
204
263
  - lib/flowcommerce_spree/version.rb
205
- - lib/flowcommerce_spree/webhook_service.rb
206
- - lib/simple_csv_writer.rb
207
264
  - lib/tasks/flowcommerce_spree.rake
208
265
  homepage: https://github.com/mejuri-inc/flowcommerce_spree
209
266
  licenses:
210
267
  - MIT
211
268
  metadata: {}
212
- post_install_message:
269
+ post_install_message:
213
270
  rdoc_options: []
214
271
  require_paths:
215
272
  - lib
@@ -224,8 +281,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
281
  - !ruby/object:Gem::Version
225
282
  version: '0'
226
283
  requirements: []
227
- rubygems_version: 3.0.8
228
- signing_key:
284
+ rubygems_version: 3.1.2
285
+ signing_key:
229
286
  specification_version: 4
230
287
  summary: Integration of Spree with Flow API
231
288
  test_files: []