flowcommerce_spree 0.0.1 → 0.0.6

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.
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,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spree
4
- OrderMailer.class_eval do
5
- # default from: ApplicationMailer::DEFAULT_FROM
6
-
7
- def refund_complete_email(web_hook_event)
8
- auth_id = web_hook_event.dig('refund', 'authorization', 'key')
9
-
10
- raise Flow::Error, 'authorization key not found in WebHookEvent [refund_capture_upserted_v2]' unless auth_id
11
-
12
- authorization = FlowcommerceSpree.client.authorizations.get_by_key FlowcommerceSpree::ORGANIZATION, auth_id
13
-
14
- refund_requested = web_hook_event['refund']['requested']
15
- @mail_to = authorization.customer.email
16
- @full_name = "#{authorization.customer.name.first} #{authorization.customer.name.last}"
17
- @amount = "#{refund_requested['amount']} #{refund_requested['currency']}"
18
- @number = authorization.order.number
19
- @order = Spree::Order.find_by number: @number
20
-
21
- mail(to: @mail_to, subject: "We refunded your order for ammount #{@amount}")
22
- end
23
- end
24
- end
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Flow.io (2017)
4
- # adapter for Spree that talks to activemerchant_flow
5
- module Spree
6
- class Gateway
7
- class Flow < Gateway
8
- def provider_class
9
- self.class
10
- end
11
-
12
- def actions
13
- %w[capture authorize purchase refund void]
14
- end
15
-
16
- # if user wants to force auto capture
17
- def auto_capture?
18
- false
19
- end
20
-
21
- def payment_profiles_supported?
22
- true
23
- end
24
-
25
- def method_type
26
- 'gateway'
27
- end
28
-
29
- def preferences
30
- {}
31
- end
32
-
33
- def supports?(source)
34
- # flow supports credit cards
35
- source.class == Spree::CreditCard
36
- end
37
-
38
- def authorize(_amount, _payment_method, options = {})
39
- order = load_order options
40
- order.cc_authorization
41
- end
42
-
43
- def capture(_amount, _payment_method, options = {})
44
- order = load_order options
45
- order.cc_capture
46
- end
47
-
48
- def purchase(_amount, _payment_method, options = {})
49
- order = load_order options
50
- flow_auth = order.cc_authorization
51
-
52
- if flow_auth.success?
53
- order.cc_capture
54
- else
55
- flow_auth
56
- end
57
- end
58
-
59
- def refund(_money, _authorization_key, options = {})
60
- order = load_order options
61
- order.cc_refund
62
- end
63
-
64
- def void(money, authorization_key, options = {})
65
- # binding.pry
66
- end
67
-
68
- def create_profile(payment)
69
- # binding.pry
70
-
71
- # payment.order.state
72
- @credit_card = payment.source
73
-
74
- profile_ensure_payment_method_is_present!
75
- create_flow_cc_profile!
76
- end
77
-
78
- private
79
-
80
- # hard inject Flow as payment method unless defined
81
- def profile_ensure_payment_method_is_present!
82
- return if @credit_card.payment_method_id
83
-
84
- flow_payment = Spree::PaymentMethod.where(active: true, type: 'Spree::Gateway::Flow').first
85
- @credit_card.payment_method_id = flow_payment.id if flow_payment
86
- end
87
-
88
- # create payment profile with Flow and tokenize Credit Card
89
- def create_flow_cc_profile!
90
- return if @credit_card.gateway_customer_profile_id
91
- return unless @credit_card.verification_value
92
-
93
- # build credit card hash
94
- data = {}
95
- data[:number] = @credit_card.number
96
- data[:name] = @credit_card.name
97
- data[:cvv] = @credit_card.verification_value
98
- data[:expiration_year] = @credit_card.year.to_i
99
- data[:expiration_month] = @credit_card.month.to_i
100
-
101
- # tokenize with Flow
102
- # rescue Io::Flow::V0::HttpClient::ServerError
103
- card_form = ::Io::Flow::V0::Models::CardForm.new(data)
104
- result = FlowcommerceSpree.client.cards.post(::FlowcommerceSpree::ORGANIZATION, card_form)
105
-
106
- @credit_card.update_column :gateway_customer_profile_id, result.token
107
- end
108
-
109
- def load_order(options)
110
- order_number = options[:order_id].split('-').first
111
- spree_order = Spree::Order.find_by number: order_number
112
- ::Flow::SimpleGateway.new spree_order
113
- end
114
- end
115
- end
116
- end
@@ -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
@@ -1,179 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # `:display_total` modifications to display total prices beside Spree default. Example: https://i.imgur.com/7v2ix2G.png
4
- module Spree # rubocop:disable Metrics/ModuleLength
5
- # Added flow specific methods to Spree::Order
6
- Order.class_eval do
7
- serialize :meta, ActiveRecord::Coders::JSON.new(symbolize_keys: true)
8
-
9
- store_accessor :meta, :flow_data
10
-
11
- before_save :sync_to_flow_io
12
- after_touch :sync_to_flow_io
13
-
14
- def sync_to_flow_io
15
- return unless zone&.flow_io_active_experience? && state == 'cart' && line_items.size > 0
16
-
17
- flow_io_order = FlowcommerceSpree::OrderSync.new(order: self)
18
- flow_io_order.build_flow_request
19
- flow_io_order.synchronize! if flow_data['digest'] != flow_io_order.digest
20
- end
21
-
22
- def display_total
23
- price = FlowcommerceSpree::Api.format_default_price total
24
- price += " (#{flow_total})" if flow_order
25
- price.html_safe
26
- end
27
-
28
- def flow_order
29
- return unless flow_data&.[]('order')
30
-
31
- Hashie::Mash.new flow_data['order']
32
- end
33
-
34
- # accepts line item, usually called from views
35
- def flow_line_item_price(line_item, total = false)
36
- result = if flow_order
37
- id = line_item.variant.sku
38
-
39
- lines = flow_order.lines || []
40
- item = lines.find { |el| el['item_number'] == id }
41
-
42
- return 'n/a' unless item
43
-
44
- total ? item['total']['label'] : item['price']['label']
45
- else
46
- FlowcommerceSpree::Api.format_default_price(line_item.price * (total ? line_item.quantity : 1))
47
- end
48
-
49
- # add line item promo
50
- # promo_total, adjustment_total
51
- result += " (#{FlowcommerceSpree::Api.format_default_price(line_item.promo_total)})" if line_item.promo_total > 0
52
-
53
- result
54
- end
55
-
56
- # prepares array of prices that can be easily renderd in templates
57
- def flow_cart_breakdown
58
- prices = []
59
-
60
- price_model = Struct.new(:name, :label)
61
-
62
- if flow_order
63
- # duty, vat, ...
64
- unless flow_order.prices
65
- message = Flow::Error.format_order_message flow_order
66
- raise Flow::Error, message
67
- end
68
-
69
- flow_order.prices.each do |price|
70
- prices.push price_model.new(price['name'], price['label'])
71
- end
72
- else
73
- price_elements =
74
- %i[item_total adjustment_total included_tax_total additional_tax_total tax_total shipment_total promo_total]
75
- price_elements.each do |el|
76
- price = send(el)
77
- if price > 0
78
- label = FlowcommerceSpree::Api.format_default_price price
79
- prices.push price_model.new(el.to_s.humanize.capitalize, label)
80
- end
81
- end
82
-
83
- # discount is applied and we allways show it in default currency
84
- if adjustment_total != 0
85
- formated_discounted_price = FlowcommerceSpree::Api.format_default_price adjustment_total
86
- prices.push price_model.new('Discount', formated_discounted_price)
87
- end
88
- end
89
-
90
- # total
91
- prices.push price_model.new(Spree.t(:total), flow_total)
92
-
93
- prices
94
- end
95
-
96
- # shows localized total, if possible. if not, fall back to Spree default
97
- def flow_total
98
- # r flow_order.total.label
99
- price = flow_order&.total&.label
100
- price || FlowcommerceSpree::Api.format_default_price(total)
101
- end
102
-
103
- def flow_experience
104
- model = Struct.new(:key)
105
- model.new flow_order.experience.key
106
- rescue StandardError => _e
107
- model.new ENV.fetch('FLOW_BASE_COUNTRY')
108
- end
109
-
110
- def flow_io_experience_key
111
- flow_data&.[]('exp')
112
- end
113
-
114
- def flow_io_experience_from_zone
115
- self.flow_data = (flow_data || {}).merge!('exp' => zone.flow_io_experience)
116
- end
117
-
118
- def flow_io_order_id
119
- flow_data&.dig('order', 'id')
120
- end
121
-
122
- def flow_io_attributes
123
- flow_data&.dig('order', 'attributes') || {}
124
- end
125
-
126
- def add_user_consent_to_flow_data(consent, value)
127
- self.flow_data['order'] ||= {}
128
- self.flow_data['order']['attributes'] ||= {}
129
- self.flow_data['order']['attributes'][consent] = value
130
- end
131
-
132
- def add_user_uuid_to_flow_data
133
- self.flow_data['order'] ||= {}
134
- self.flow_data['order']['attributes'] ||= {}
135
- self.flow_data['order']['attributes']['user_uuid'] = user&.uuid
136
- end
137
-
138
- def flow_io_user_uuid
139
- flow_data&.dig('order', 'attributes', 'user_uuid')
140
- end
141
-
142
- def checkout_url
143
- "https://checkout.flow.io/#{FlowcommerceSpree::ORGANIZATION}/checkout/#{number}/" \
144
- "contact-info?flow_session_id=#{flow_data['session_id']}"
145
- end
146
-
147
- # clear invalid zero amount payments. Solidus bug?
148
- def clear_zero_amount_payments!
149
- # class attribute that can be set to true
150
- return unless Flow::Order.clear_zero_amount_payments
151
-
152
- payments.where(amount: 0, state: %w[invalid processing pending]).map(&:destroy)
153
- end
154
-
155
- def flow_order_authorized?
156
- flow_data&.[]('authorization') ? true : false
157
- end
158
-
159
- def flow_order_captured?
160
- flow_data['capture'] ? true : false
161
- end
162
-
163
- # completes order and sets all states to finalized and complete
164
- # used when we have confirmed capture from Flow API or PayPal
165
- def flow_finalize!
166
- finalize! unless state == 'complete'
167
- update_column :payment_state, 'paid' if payment_state != 'paid'
168
- update_column :state, 'complete' if state != 'complete'
169
- end
170
-
171
- def flow_payment_method
172
- if flow_data['payment_type'] == 'paypal'
173
- 'paypal'
174
- else
175
- 'cc' # creait card is default
176
- end
177
- end
178
- end
179
- end
@@ -1,86 +0,0 @@
1
- <%
2
- # render text email in console. parts[1] for html body
3
- # puts Spree::OrderMailer.confirm_email(Spree::Order.last).body.parts[0].body
4
-
5
- @prices = @order.flow_cart_breakdown
6
- @total_price = @prices.pop
7
- %>
8
-
9
- <style>
10
- table.order td { padding: 4px; border-top: 1px solid #bbb; }
11
- </style>
12
-
13
- <h6>Dear <%= @order.bill_address.firstname %></h6>
14
-
15
- <br>
16
- <br>
17
-
18
- <p><%= Spree.t('order_mailer.confirm_email.instructions') %></p>
19
- <p><%= Spree.t('order_mailer.confirm_email.order_summary') %></p>
20
-
21
- <table class="order">
22
- <tr>
23
- <th>Product</th>
24
- <th width="100" align="right">Price</th>
25
- <th width="80" align="center">Quantity</th>
26
- <th width="100" align="right">Total</th>
27
- </tr>
28
- <% @order.line_items.each do |line_item| %>
29
- <tr>
30
- <td><%= line_item.variant.product.name %></td>
31
- <td align="right"><%= @order.flow_line_item_price(line_item) %></td>
32
- <td align="center"><%= line_item.quantity %></td>
33
- <td align="right"><%= @order.flow_line_item_price(line_item, :with_quantity) %></td>
34
- </tr>
35
- <% end %>
36
- </table>
37
-
38
- <br>
39
-
40
- <p><b>Total</b></p>
41
-
42
- <table class="order">
43
-
44
- <% @prices.each do |price| %>
45
- <tr><td width="120"><%= price.name.capitalize %></td><td align="right"><%= price.label %></td></tr>
46
- <% end %>
47
-
48
- <tr>
49
- <td><%= Spree.t(:total) %></td>
50
- <td align="right"><b><%= @total_price.label %></b></td>
51
- </tr>
52
- <tr>
53
- <td>Payment method</td>
54
- <td align="right"><%= @order.flow_payment_method == 'paypal' ? 'PayPal' : 'Credit Card' %></td>
55
- </tr>
56
- </table>
57
-
58
- <br>
59
-
60
- <% ['ship', 'bill'].each do |name|
61
- address = @order.send('%s_address' % name)
62
- %>
63
- <p><b><%= name.capitalize %>ing address</b></p>
64
-
65
- <table class="order">
66
- <tr>
67
- <td>Full name</td>
68
- <td><%= address.firstname %> <%= address.lastname %></td>
69
- </tr>
70
- <tr>
71
- <td>Address</td>
72
- <td><%= address.address1 %></td>
73
- </tr>
74
- <tr>
75
- <td>City</td>
76
- <td><%= address.city %></td>
77
- </tr>
78
- <tr>
79
- <td>Country</td>
80
- <td><%= address.country.name rescue '-' %>, <%= address.state.name rescue '-' %></td>
81
- </tr>
82
- </table>
83
- <br />
84
- <% end %>
85
-
86
- <p><%= Spree.t('order_mailer.confirm_email.thanks') %></p>
@@ -1,38 +0,0 @@
1
- <%= Spree.t('order_mailer.confirm_email.dear_customer') %>
2
-
3
- <%= Spree.t('order_mailer.confirm_email.instructions') %>
4
-
5
- ============================================================
6
- <%= Spree.t('order_mailer.confirm_email.order_summary') %>
7
- ============================================================
8
- <% @order.line_items.each do |item| %>
9
- <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= @order.flow_line_item_price(line_item, :with_quantity) %>
10
- <% end %>
11
- ============================================================
12
- <%= Spree.t('order_mailer.confirm_email.subtotal', :subtotal => @order.display_item_total) %>
13
- <% if @order.line_item_adjustments.exists? %>
14
- <% if @order.all_adjustments.promotion.eligible.exists? %>
15
- <% @order.all_adjustments.promotion.eligible.group_by(&:label).each do |label, adjustments| %>
16
- <%= Spree.t(:promotion) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
17
- <% end %>
18
- <% end %>
19
- <% end %>
20
-
21
- <% @order.shipments.group_by { |s| s.selected_shipping_rate.try(:name) }.each do |name, shipments| %>
22
- <%= Spree.t(:shipping) %>: <%= name %> <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: @order.currency) %>
23
- <% end %>
24
-
25
- <% if @order.all_adjustments.eligible.tax.exists? %>
26
- <% @order.all_adjustments.eligible.tax.group_by(&:label).each do |label, adjustments| %>
27
- <%= Spree.t(:tax) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
28
- <% end %>
29
- <% end %>
30
-
31
- <% @order.adjustments.eligible.each do |adjustment| %>
32
- <% next if (adjustment.source_type == 'Spree::TaxRate') and (adjustment.amount == 0) %>
33
- <%= adjustment.label %> <%= adjustment.display_amount %>
34
- <% end %>
35
- ============================================================
36
- <%= Spree.t('order_mailer.confirm_email.total', :total => @order.display_total) %>
37
-
38
- <%= Spree.t('order_mailer.confirm_email.thanks') %>