flowcommerce_spree 0.0.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +101 -24
  3. data/SPREE_FLOW.md +6 -28
  4. data/app/controllers/concerns/current_zone_loader_decorator.rb +40 -25
  5. data/app/controllers/flowcommerce_spree/inventory_controller.rb +23 -0
  6. data/app/controllers/flowcommerce_spree/orders_controller.rb +20 -0
  7. data/app/controllers/flowcommerce_spree/webhooks_controller.rb +23 -13
  8. data/app/controllers/users/sessions_controller_decorator.rb +28 -0
  9. data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +4 -9
  10. data/app/models/spree/address_decorator.rb +19 -0
  11. data/app/models/spree/app_configuration_decorator.rb +7 -0
  12. data/app/models/spree/calculator/flow_io.rb +61 -0
  13. data/app/models/spree/calculator/shipping/flow_io.rb +40 -0
  14. data/app/models/spree/flow_io_credit_card_decorator.rb +21 -0
  15. data/app/models/spree/flow_io_order_decorator.rb +163 -0
  16. data/app/models/spree/flow_io_product_decorator.rb +2 -2
  17. data/app/models/spree/flow_io_variant_decorator.rb +4 -2
  18. data/app/models/spree/gateway/flow_io.rb +153 -0
  19. data/app/models/spree/{credit_card_decorator.rb → payment_capture_event_decorator.rb} +1 -1
  20. data/app/models/spree/promotion_handler/coupon_decorator.rb +1 -1
  21. data/app/models/spree/zones/flow_io_product_zone_decorator.rb +8 -0
  22. data/app/models/tracking/setup_decorator.rb +40 -0
  23. data/app/overrides/spree/admin/order_sidebar_summary_flow_link.rb +13 -0
  24. data/app/overrides/spree/admin/products/order_price_flow_message.rb +9 -0
  25. data/app/serializers/api/v2/order_serializer_decorator.rb +20 -0
  26. data/app/services/flowcommerce_spree/import_experience_items.rb +1 -1
  27. data/app/services/flowcommerce_spree/order_sync.rb +81 -173
  28. data/app/services/flowcommerce_spree/order_updater.rb +78 -0
  29. data/app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb +76 -0
  30. data/app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb +66 -0
  31. data/app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb +25 -0
  32. data/app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb +35 -0
  33. data/app/services/flowcommerce_spree/webhooks/local_item_upserted.rb +40 -0
  34. data/app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb +21 -0
  35. data/config/rails_best_practices.yml +51 -0
  36. data/config/routes.rb +3 -1
  37. data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +6 -4
  38. data/lib/flow/simple_gateway.rb +0 -36
  39. data/lib/flowcommerce_spree.rb +17 -3
  40. data/lib/flowcommerce_spree/engine.rb +33 -3
  41. data/lib/flowcommerce_spree/experience_service.rb +1 -27
  42. data/lib/flowcommerce_spree/logging_http_client.rb +33 -15
  43. data/lib/flowcommerce_spree/session.rb +16 -29
  44. data/lib/flowcommerce_spree/test_support.rb +7 -0
  45. data/lib/flowcommerce_spree/version.rb +1 -1
  46. data/lib/tasks/flowcommerce_spree.rake +4 -1
  47. metadata +90 -22
  48. data/app/mailers/spree/spree_order_mailer_decorator.rb +0 -24
  49. data/app/models/spree/gateway/spree_flow_gateway.rb +0 -116
  50. data/app/models/spree/line_item_decorator.rb +0 -15
  51. data/app/models/spree/order_decorator.rb +0 -179
  52. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -86
  53. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -38
  54. data/config/initializers/flowcommerce_spree.rb +0 -7
  55. data/lib/flow/error.rb +0 -73
  56. data/lib/flow/pay_pal.rb +0 -25
  57. data/lib/flowcommerce_spree/webhook_service.rb +0 -98
  58. data/lib/simple_csv_writer.rb +0 -44
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FlowcommerceSpree
4
- ORGANIZATION = ENV.fetch('FLOW_ORGANIZATION', 'flow.io')
5
- BASE_COUNTRY = ENV.fetch('FLOW_BASE_COUNTRY', 'USA')
6
- API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
7
- end
data/lib/flow/error.rb DELETED
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Flow (2017)
4
- # api error logger and formater
5
-
6
- require 'digest/sha1'
7
-
8
- class Flow::Error < StandardError
9
- # logs error to file for easy discovery and fix
10
- def self.log(exception, request)
11
- history = exception.backtrace.reject { |el| el.index('/gems/') }.map { |el| el.sub(Rails.root.to_s, '') }.join($/)
12
-
13
- msg = "#{exception.class} in #{request.url}"
14
- data = [msg, exception.message, history].join("\n\n")
15
- key = Digest::SHA1.hexdigest(exception.backtrace.first.split(' ').first)
16
-
17
- folder = Rails.root.join('log/exceptions').to_s
18
- Dir.mkdir(folder) unless Dir.exist?(folder)
19
-
20
- folder += "/#{exception.class.to_s.tableize.gsub('/', '-')}"
21
- Dir.mkdir(folder) unless Dir.exist?(folder)
22
-
23
- "#{folder}/#{key}.txt".tap do |path|
24
- File.write(path, data)
25
- end
26
- end
27
-
28
- def self.format_message(exception)
29
- # format Flow errors in a special way
30
- # Io::Flow::V0::HttpClient::ServerError - 422 Unprocessable Entity:
31
- # {"code":"invalid_number","messages":["Card number is not valid"]}
32
- # hash['code'] = 'invalid_number'
33
- # hash['message'] = 'Card number is not valid'
34
- # hash['title'] = '422 Unprocessable Entity'
35
- # hash['klass'] = 'Io::Flow::V0::HttpClient::ServerError'
36
- if exception.class == Io::Flow::V0::HttpClient::ServerError
37
- parts = exception.message.split(': ', 2)
38
- hash = Oj.load(parts[1])
39
-
40
- hash[:message] = hash['messages'].join(', ')
41
- hash[:title] = parts[0]
42
- hash[:klass] = exception.class
43
- hash[:code] = hash['code']
44
- else
45
- msg = exception.message.is_a?(Array) ? exception.message.join(' - ') : exception.message
46
-
47
- hash = {}
48
- hash[:message] = msg
49
- hash[:title] = '-'
50
- hash[:klass] = exception.class
51
- hash[:code] = '-'
52
- end
53
-
54
- hash
55
- end
56
-
57
- def self.format_order_message(order)
58
- message = if order['messages']
59
- msg = order['messages'].join(', ')
60
- msg += " (#{Spree::Variant.where(id: order['numbers']).map(&:name).join(', ')})" if order['numbers']
61
- msg
62
- else
63
- 'Order not properly localized (sync issue)'
64
- end
65
-
66
- # sub_info = 'Flow.io'
67
- # sub_info += ' - %s' % flow_experience.key[0, 15] if flow_experience
68
-
69
- # '%s (%s)' % [message, sub_info]
70
-
71
- message
72
- end
73
- end
data/lib/flow/pay_pal.rb DELETED
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Flow.io (2017)
4
- # communicates with flow api to synchronize Spree order with PayPal
5
-
6
- module Flow::PayPal
7
- extend self
8
-
9
- def get_id(order)
10
- raise 'PayPal only supported while using flow' unless order.flow_order
11
-
12
- # get PayPal ID using Flow api
13
- body = {
14
- # discriminator: 'merchant_of_record_payment_form',
15
- method: 'paypal',
16
- order_number: order.number,
17
- amount: order.flow_order.total.amount,
18
- currency: order.flow_order.total.currency
19
- }
20
-
21
- # FlowcommerceSpree::Api.run :post, '/:organization/payments', {}, body
22
- form = ::Io::Flow::V0::Models::MerchantOfRecordPaymentForm.new body
23
- FlowcommerceSpree.client.payments.post FlowcommerceSpree::ORGANIZATION, form
24
- end
25
- end
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FlowcommerceSpree
4
- # communicates with flow api, responds to webhook events
5
- class WebhookService
6
- attr_accessor :errors, :product, :variant
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
- org = @data['organization']
21
- if org != ORGANIZATION
22
- errors << { message: "Organization name mismatch for #{org}" }
23
- else
24
- discriminator = @data['discriminator']
25
- hook_method = "hook_#{discriminator}"
26
- # If hook processing method registered an error, a self.object of WebhookService with this error will be
27
- # returned, else an ActiveRecord object will be returned
28
- return __send__(hook_method) if respond_to?(hook_method, true)
29
-
30
- errors << { message: "No hook for #{discriminator}" }
31
- end
32
-
33
- self
34
- end
35
-
36
- private
37
-
38
- def hook_experience_upserted_v2
39
- experience = @data['experience']
40
- Spree::Zones::Product.find_or_initialize_by(name: experience['key'].titleize).store_flow_io_data(experience)
41
- end
42
-
43
- def hook_local_item_upserted
44
- if (local_item = @data['local_item'])
45
- if (received_sku = local_item.dig('item', 'number'))
46
- if (@variant = Spree::Variant.find_by(sku: received_sku))
47
- @variant.add_flow_io_experience_data(
48
- local_item.dig('experience', 'key'),
49
- 'prices' => [local_item.dig('pricing', 'price')], 'status' => local_item['status']
50
- )
51
-
52
- @variant.update_column(:meta, @variant.meta.to_json)
53
- return @variant
54
- else
55
- errors << { message: "Variant with sku [#{received_sku}] not found!" }
56
- end
57
- else
58
- errors << { message: 'SKU param missing' }
59
- end
60
- else
61
- errors << { message: 'Local item param missing' }
62
- end
63
-
64
- self
65
- end
66
-
67
- def hook_order_upserted_v2
68
- errors << { message: 'Order param missing' } unless (received_order = @data['order'])
69
-
70
- if errors.none? && (order_number = received_order['number'])
71
- if (order = Spree::Order.find_by(number: order_number))
72
- order.flow_data['order'] = received_order.to_hash
73
- attrs_to_update = { meta: order.meta.to_json }
74
- if order.flow_data['order']['submitted_at'].present?
75
- attrs_to_update[:state] = 'complete'
76
- attrs_to_update[:completed_at] = Time.zone.now
77
- end
78
-
79
- order.update_columns(attrs_to_update)
80
- return order
81
- else
82
- errors << { message: "Order #{order_number} not found" }
83
- end
84
- else
85
- errors << { message: 'Order number param missing' }
86
- end
87
-
88
- self
89
- end
90
-
91
- # send en email when order is refunded
92
- def hook_refund_upserted_v2
93
- Spree::OrderMailer.refund_complete_email(@data).deliver
94
-
95
- 'Email delivered'
96
- end
97
- end
98
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # simple class to build scv files
4
-
5
- # csv = CsvWriter.new
6
- # csv.add a: 1, b: 'a', c: '"a'
7
- # csv.add a: ',', b: 'foo, bar'
8
- # csv.to_s
9
-
10
- class SimpleCsvWriter
11
- def initialize(delimiter: nil)
12
- @data = []
13
- @delimiter = delimiter || "\t"
14
- end
15
-
16
- # add hash or list
17
- def add(data)
18
- list = if data.class == Hash
19
- @keys ||= data.keys
20
- @keys.map { |key| data[key] }
21
- else
22
- data
23
- end
24
-
25
- @data.push list.map { |el| fmt(el) }.join(@delimiter)
26
- end
27
-
28
- def to_s
29
- if @keys
30
- @keys.map(&:to_s).join(@delimiter) + "\n" +
31
- @data.join($RS)
32
- else
33
- @data.join($RS)
34
- end
35
- end
36
-
37
- private
38
-
39
- def fmt(item)
40
- item = item.to_s.gsub($RS, '\\n').gsub('"', '""')
41
-
42
- item.include?(@delimiter) || item.include?('\\') ? "\"#{item}\"" : item
43
- end
44
- end