flowcommerce_spree 0.0.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +91 -0
- data/Rakefile +33 -0
- data/SPREE_FLOW.md +134 -0
- data/app/assets/javascripts/flowcommerce_spree/application.js +13 -0
- data/app/assets/stylesheets/flowcommerce_spree/application.css +15 -0
- data/app/controllers/concerns/current_zone_loader_decorator.rb +49 -0
- data/app/controllers/flowcommerce_spree/webhooks_controller.rb +25 -0
- data/app/helpers/flowcommerce_spree/application_helper.rb +6 -0
- data/app/helpers/spree/admin/orders_helper_decorator.rb +17 -0
- data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +53 -0
- data/app/mailers/spree/spree_order_mailer_decorator.rb +24 -0
- data/app/models/flowcommerce_spree/settings.rb +8 -0
- data/app/models/spree/credit_card_decorator.rb +9 -0
- data/app/models/spree/flow_io_product_decorator.rb +91 -0
- data/app/models/spree/flow_io_variant_decorator.rb +205 -0
- data/app/models/spree/gateway/spree_flow_gateway.rb +116 -0
- data/app/models/spree/line_item_decorator.rb +15 -0
- data/app/models/spree/order_decorator.rb +179 -0
- data/app/models/spree/promotion_decorator.rb +10 -0
- data/app/models/spree/promotion_handler/coupon_decorator.rb +30 -0
- data/app/models/spree/spree_user_decorator.rb +15 -0
- data/app/models/spree/taxon_decorator.rb +37 -0
- data/app/models/spree/zone_decorator.rb +7 -0
- data/app/models/spree/zones/flow_io_product_zone_decorator.rb +55 -0
- data/app/services/flowcommerce_spree/import_experience_items.rb +76 -0
- data/app/services/flowcommerce_spree/import_experiences.rb +37 -0
- data/app/services/flowcommerce_spree/order_sync.rb +231 -0
- data/app/views/layouts/flowcommerce_spree/application.html.erb +14 -0
- data/app/views/spree/admin/payments/index.html.erb +28 -0
- data/app/views/spree/admin/promotions/edit.html.erb +57 -0
- data/app/views/spree/admin/shared/_order_summary.html.erb +44 -0
- data/app/views/spree/admin/shared/_order_summary_flow.html.erb +13 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +86 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +38 -0
- data/config/initializers/flowcommerce_spree.rb +7 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20201021160159_add_type_and_meta_to_spree_zone.rb +23 -0
- data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +17 -0
- data/db/migrate/20201022173210_add_zone_type_to_spree_zone_members.rb +24 -0
- data/db/migrate/20201022174252_add_kind_to_zone.rb +22 -0
- data/lib/flow/error.rb +73 -0
- data/lib/flow/pay_pal.rb +25 -0
- data/lib/flow/simple_gateway.rb +115 -0
- data/lib/flowcommerce_spree.rb +31 -0
- data/lib/flowcommerce_spree/api.rb +48 -0
- data/lib/flowcommerce_spree/engine.rb +27 -0
- data/lib/flowcommerce_spree/experience_service.rb +65 -0
- data/lib/flowcommerce_spree/logging_http_client.rb +43 -0
- data/lib/flowcommerce_spree/logging_http_handler.rb +15 -0
- data/lib/flowcommerce_spree/refresher.rb +81 -0
- data/lib/flowcommerce_spree/session.rb +71 -0
- data/lib/flowcommerce_spree/version.rb +5 -0
- data/lib/flowcommerce_spree/webhook_service.rb +98 -0
- data/lib/simple_csv_writer.rb +44 -0
- data/lib/tasks/flowcommerce_spree.rake +289 -0
- metadata +220 -0
@@ -0,0 +1,24 @@
|
|
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
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Flow specific methods for Spree::Product
|
4
|
+
module Spree
|
5
|
+
module FlowIoProductDecorator
|
6
|
+
def self.prepended(base)
|
7
|
+
base.serialize :meta, ActiveRecord::Coders::JSON.new(symbolize_keys: true)
|
8
|
+
|
9
|
+
base.store_accessor :meta, :flow_data, :zone_ids
|
10
|
+
end
|
11
|
+
|
12
|
+
def price_in_zone(currency, product_zone)
|
13
|
+
flow_experience_key = product_zone&.flow_data&.[]('key')
|
14
|
+
return flow_local_price(flow_experience_key) if flow_experience_key.present?
|
15
|
+
|
16
|
+
price_in(currency)
|
17
|
+
end
|
18
|
+
|
19
|
+
# returns price bound to local experience from master variant
|
20
|
+
def flow_local_price(flow_exp)
|
21
|
+
master.flow_local_price(flow_exp) || Spree::Price.new(variant_id: id, currency: 'USD', amount: 0)
|
22
|
+
end
|
23
|
+
|
24
|
+
def flow_included?(flow_exp)
|
25
|
+
return true unless flow_exp
|
26
|
+
|
27
|
+
flow_data["#{flow_exp.key}.excluded"].to_i != 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def price_range(product_zone)
|
31
|
+
prices = {}
|
32
|
+
master_prices.each do |p|
|
33
|
+
currency = p.currency
|
34
|
+
min = nil
|
35
|
+
max = nil
|
36
|
+
|
37
|
+
if variants.any?
|
38
|
+
variants.each do |v|
|
39
|
+
price = v.price_in(currency)
|
40
|
+
next if price.nil? || price.amount.nil?
|
41
|
+
|
42
|
+
min = price if min.nil? || min.amount > price.amount
|
43
|
+
max = price if max.nil? || max.amount < price.amount
|
44
|
+
end
|
45
|
+
else
|
46
|
+
min = max = master.price_in(currency)
|
47
|
+
end
|
48
|
+
|
49
|
+
rmin = min&.amount&.to_s(:rounded, precision: 0) || 0
|
50
|
+
rmax = max&.amount&.to_s(:rounded, precision: 0) || 0
|
51
|
+
|
52
|
+
prices[currency] = rmin == rmax ? { amount: rmin } : { min: rmin, max: rmax }
|
53
|
+
end
|
54
|
+
|
55
|
+
add_flow_price_range(prices, product_zone)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_flow_price_range(prices, product_zone)
|
59
|
+
flow_experience_key = product_zone&.flow_data&.[]('key')
|
60
|
+
return prices if flow_experience_key.blank?
|
61
|
+
|
62
|
+
master_price = master.flow_local_price(flow_experience_key)
|
63
|
+
currency = product_zone.flow_io_experience_currency
|
64
|
+
min = nil
|
65
|
+
max = nil
|
66
|
+
|
67
|
+
if variants.any?
|
68
|
+
variants.each do |v|
|
69
|
+
price = v.flow_local_price(flow_experience_key)
|
70
|
+
next if price.amount.nil? || price.currency != currency
|
71
|
+
|
72
|
+
min = price if min.nil? || min.amount > price.amount
|
73
|
+
max = price if max.nil? || max.amount < price.amount
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if master_price.currency == currency
|
78
|
+
min ||= master_price
|
79
|
+
max ||= master_price
|
80
|
+
end
|
81
|
+
|
82
|
+
rmin = min&.amount&.to_s(:rounded, precision: 0) || 0
|
83
|
+
rmax = max&.amount&.to_s(:rounded, precision: 0) || 0
|
84
|
+
|
85
|
+
prices[currency] = rmin == rmax ? { amount: rmin } : { min: rmin, max: rmax }
|
86
|
+
prices
|
87
|
+
end
|
88
|
+
|
89
|
+
Spree::Product.prepend(self) if Spree::Product.included_modules.exclude?(self)
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Flow specific methods for Spree::Variant
|
4
|
+
# Spree save all the prices inside Variant object. We choose to have a cache jsonb field named flow_data that will
|
5
|
+
# hold all important Flow sync data for specific experiences.
|
6
|
+
module Spree
|
7
|
+
module FlowIoVariantDecorator
|
8
|
+
def self.prepended(base)
|
9
|
+
base.serialize :meta, ActiveRecord::Coders::JSON.new(symbolize_keys: true)
|
10
|
+
|
11
|
+
base.store_accessor :meta, :flow_data
|
12
|
+
|
13
|
+
# after every save we sync product we generate sh1 checksums to update only when change happend
|
14
|
+
base.after_save :sync_product_to_flow
|
15
|
+
end
|
16
|
+
|
17
|
+
def experiences
|
18
|
+
flow_data&.[]('exp')
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_flow_io_experience_data(exp, value)
|
22
|
+
raise ArgumentError, 'Value should be a hash' unless value.is_a?(Hash)
|
23
|
+
|
24
|
+
self.flow_data = flow_data || {}
|
25
|
+
self.flow_data['exp'] ||= {}
|
26
|
+
self.flow_data['exp'][exp] = value
|
27
|
+
end
|
28
|
+
|
29
|
+
# clears flow_data from the records
|
30
|
+
def truncate_flow_data
|
31
|
+
flow_data&.[]('exp')&.keys&.each do |exp_key|
|
32
|
+
break unless (product = self.product)
|
33
|
+
|
34
|
+
remove_experience_from_product(exp_key, product)
|
35
|
+
end
|
36
|
+
|
37
|
+
meta.delete(:flow_data)
|
38
|
+
update_column(:meta, meta.to_json)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_experience_from_product(exp_key, product)
|
42
|
+
return unless (zone = Spree::Zones::Product.find_by(name: exp_key.titleize))
|
43
|
+
|
44
|
+
zone_ids = product.zone_ids || []
|
45
|
+
zone_id_string = zone.id.to_s
|
46
|
+
return unless zone_ids.include?(zone_id_string)
|
47
|
+
|
48
|
+
product.zone_ids = zone_ids - [zone_id_string]
|
49
|
+
product.update_columns(meta: product.meta.to_json)
|
50
|
+
end
|
51
|
+
|
52
|
+
# upload product variant to Flow's Product Catalog
|
53
|
+
def sync_product_to_flow
|
54
|
+
# initial Spree seed will fail, so skip unless we have Flow data field
|
55
|
+
return unless respond_to?(:flow_data)
|
56
|
+
|
57
|
+
return if FlowcommerceSpree::API_KEY.blank? || FlowcommerceSpree::API_KEY == 'test_key'
|
58
|
+
|
59
|
+
return { error: 'Price is 0' } if price == 0
|
60
|
+
|
61
|
+
additional_attrs = {}
|
62
|
+
attr_name = nil
|
63
|
+
export_required = false
|
64
|
+
FlowcommerceSpree::Config.additional_attributes[self.class.name.tableize.tr('/', '_').to_sym]&.each do |attr_item|
|
65
|
+
attr_name = attr_item[0]
|
66
|
+
# Flow.io could require a different attribute name, as in case of Fulfil's :customs_description - it has the
|
67
|
+
# export_name `:materials` for flow.io. That's why 1st we're checking if an export_name is defined for the
|
68
|
+
# attribute.
|
69
|
+
attr_flowcommerce_name = attr_item[1][:export_name] || attr_name
|
70
|
+
export_required = attr_item[1][:export] == :required
|
71
|
+
attr_value = __send__(attr_name)
|
72
|
+
break if export_required && attr_value.blank?
|
73
|
+
|
74
|
+
additional_attrs[attr_flowcommerce_name] = attr_value if attr_value
|
75
|
+
end
|
76
|
+
|
77
|
+
if export_required && additional_attrs[attr_value].blank?
|
78
|
+
return { error: "Variant with sku = #{sku} has no #{attr_name}" }
|
79
|
+
end
|
80
|
+
|
81
|
+
flow_item = to_flowcommerce_item(additional_attrs)
|
82
|
+
flow_item_sh1 = Digest::SHA1.hexdigest(flow_item.to_json)
|
83
|
+
|
84
|
+
# skip if sync not needed
|
85
|
+
return nil if flow_data&.[](:last_sync_sh1) == flow_item_sh1
|
86
|
+
|
87
|
+
response = FlowcommerceSpree.client.items.put_by_number(FlowcommerceSpree::ORGANIZATION, sku, flow_item)
|
88
|
+
self.flow_data ||= {}
|
89
|
+
self.flow_data[:last_sync_sh1] = flow_item_sh1
|
90
|
+
|
91
|
+
# after successful put, write cache
|
92
|
+
update_column(:meta, meta.to_json)
|
93
|
+
|
94
|
+
response
|
95
|
+
rescue Net::OpenTimeout => e
|
96
|
+
{ error: e.message }
|
97
|
+
end
|
98
|
+
|
99
|
+
def flow_prices(flow_exp)
|
100
|
+
flow_data&.dig(:exp, flow_exp, :prices) || []
|
101
|
+
end
|
102
|
+
|
103
|
+
# returns price bound to local experience
|
104
|
+
def flow_local_price(flow_exp)
|
105
|
+
price_object = flow_prices(flow_exp)&.first
|
106
|
+
amount = price_object&.[](:amount) || price
|
107
|
+
currency = price_object&.[](:currency) || cost_currency
|
108
|
+
Spree::Price.new(variant_id: id, currency: currency, amount: amount)
|
109
|
+
end
|
110
|
+
|
111
|
+
def price_in_zone(currency, product_zone)
|
112
|
+
flow_experience_key = product_zone&.flow_data&.[]('key')
|
113
|
+
return flow_local_price(flow_experience_key) if flow_experience_key.present?
|
114
|
+
|
115
|
+
price_in(currency)
|
116
|
+
end
|
117
|
+
|
118
|
+
def all_prices_in_zone(product_zone)
|
119
|
+
all_prices = prices.map { |price| { currency: price.currency, amount: (price.amount&.round || 0).to_s } }
|
120
|
+
|
121
|
+
flow_experience_key = product_zone&.flow_data&.[]('key')
|
122
|
+
return all_prices if flow_experience_key.blank?
|
123
|
+
|
124
|
+
flow_price = flow_local_price(flow_experience_key)
|
125
|
+
all_prices << { currency: flow_price.currency, amount: (flow_price.amount&.round || 0).to_s }
|
126
|
+
all_prices
|
127
|
+
end
|
128
|
+
|
129
|
+
# creates object for flow api
|
130
|
+
def to_flowcommerce_item(additional_attrs)
|
131
|
+
# add product categories
|
132
|
+
categories = []
|
133
|
+
taxon = product.taxons.first
|
134
|
+
current_taxon = taxon
|
135
|
+
while current_taxon
|
136
|
+
categories.unshift current_taxon.name
|
137
|
+
current_taxon = current_taxon.parent
|
138
|
+
end
|
139
|
+
|
140
|
+
images = if (image = product.images.first || product.variant_images.first)
|
141
|
+
asset_host_scheme = ENV.fetch('ASSET_HOST_PROTOCOL', 'https')
|
142
|
+
asset_host = ENV.fetch('ASSET_HOST', 'staging.mejuri.com')
|
143
|
+
large_image_uri = URI(image.attachment(:large))
|
144
|
+
product_image_uri = URI(image.attachment.url(:product))
|
145
|
+
large_image_uri.scheme ||= asset_host_scheme
|
146
|
+
product_image_uri.scheme ||= asset_host_scheme
|
147
|
+
large_image_uri.host ||= asset_host
|
148
|
+
product_image_uri.host ||= asset_host
|
149
|
+
|
150
|
+
[{ url: large_image_uri.to_s, tags: ['checkout'] },
|
151
|
+
{ url: product_image_uri.to_s, tags: ['thumbnail'] }]
|
152
|
+
else
|
153
|
+
[]
|
154
|
+
end
|
155
|
+
|
156
|
+
Io::Flow::V0::Models::ItemForm.new(
|
157
|
+
number: sku,
|
158
|
+
locale: 'en_US',
|
159
|
+
language: 'en',
|
160
|
+
name: product.name,
|
161
|
+
description: product.description,
|
162
|
+
currency: cost_currency,
|
163
|
+
price: price.to_f,
|
164
|
+
images: images,
|
165
|
+
categories: categories,
|
166
|
+
attributes: common_attrs(taxon).merge!(additional_attrs)
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
def common_attrs(taxon)
|
171
|
+
{
|
172
|
+
weight: weight.to_s,
|
173
|
+
height: height.to_s,
|
174
|
+
width: width.to_s,
|
175
|
+
depth: depth.to_s,
|
176
|
+
is_master: is_master ? 'true' : 'false',
|
177
|
+
product_id: product_id.to_s,
|
178
|
+
tax_category: product.tax_category_id.to_s,
|
179
|
+
product_description: product.description,
|
180
|
+
product_shipping_category: product.shipping_category_id ? shipping_category.name : nil,
|
181
|
+
product_meta_title: taxon&.meta_title.to_s,
|
182
|
+
product_meta_description: taxon&.meta_description.to_s,
|
183
|
+
product_meta_keywords: taxon&.meta_keywords.to_s,
|
184
|
+
product_slug: product.slug
|
185
|
+
}.select { |_k, v| v.present? }
|
186
|
+
end
|
187
|
+
|
188
|
+
# gets flow catalog item, and imports it
|
189
|
+
# called from flow:sync_localized_items rake task
|
190
|
+
def flow_import_item(item_hash, experience_key: nil)
|
191
|
+
# If experience not specified, get it from the local hash of imported variant
|
192
|
+
experience_key ||= item_hash.dig(:local, :experience, :key)
|
193
|
+
current_experience_meta = item_hash.delete(:local)
|
194
|
+
|
195
|
+
# Do not repeatedly store Experience data - this is stored in Spree::Zones::Product
|
196
|
+
current_experience_meta.delete(:experience)
|
197
|
+
add_flow_io_experience_data(experience_key, current_experience_meta)
|
198
|
+
self.flow_data.merge!(item_hash)
|
199
|
+
|
200
|
+
update_column(:meta, meta.to_json)
|
201
|
+
end
|
202
|
+
|
203
|
+
Spree::Variant.prepend(self) if Spree::Variant.included_modules.exclude?(self)
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,116 @@
|
|
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
|