stall 0.1.1 → 0.1.2
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 +45 -0
- data/app/assets/javascripts/stall.coffee +2 -0
- data/app/assets/javascripts/stall/cart-form.coffee +23 -0
- data/app/controllers/stall/application_controller.rb +10 -2
- data/app/controllers/stall/carts_controller.rb +31 -10
- data/app/controllers/stall/checkout/steps_controller.rb +36 -4
- data/app/controllers/stall/checkouts_controller.rb +12 -3
- data/app/controllers/stall/line_items_controller.rb +3 -3
- data/app/controllers/stall/payments_controller.rb +5 -3
- data/app/helpers/stall/add_to_cart_helper.rb +1 -1
- data/app/helpers/stall/checkout_helper.rb +22 -2
- data/app/helpers/stall/payments_helper.rb +8 -0
- data/app/mailers/stall/admin_mailer.rb +11 -0
- data/app/mailers/stall/base_mailer.rb +17 -0
- data/app/mailers/stall/customer_mailer.rb +13 -0
- data/app/models/address.rb +3 -0
- data/app/models/address_ownership.rb +3 -0
- data/app/models/adjustment.rb +3 -0
- data/app/models/cart.rb +3 -0
- data/app/models/customer.rb +3 -0
- data/app/models/line_item.rb +3 -0
- data/app/models/payment.rb +3 -0
- data/app/models/payment_method.rb +3 -0
- data/app/models/product_list.rb +3 -0
- data/app/models/shipment.rb +3 -0
- data/app/models/shipping_method.rb +3 -0
- data/app/models/stall/models.rb +4 -0
- data/app/models/stall/models/address.rb +45 -0
- data/app/models/stall/models/address_ownership.rb +26 -0
- data/app/models/stall/models/adjustment.rb +49 -0
- data/app/models/stall/models/cart.rb +37 -0
- data/app/models/stall/models/customer.rb +18 -0
- data/app/models/stall/models/line_item.rb +57 -0
- data/app/models/stall/models/payment.rb +33 -0
- data/app/models/stall/models/payment_method.rb +18 -0
- data/app/models/stall/models/product_list.rb +147 -0
- data/app/models/stall/models/shipment.rb +29 -0
- data/app/models/stall/models/shipping_method.rb +16 -0
- data/app/services/stall/add_to_cart_service.rb +11 -1
- data/app/services/stall/cart_update_service.rb +26 -0
- data/app/services/stall/payment_notification_service.rb +23 -9
- data/app/services/stall/shipping_fee_calculator_service.rb +13 -2
- data/app/views/checkout/steps/_informations.html.haml +3 -3
- data/app/views/checkout/steps/_payment.html.haml +25 -4
- data/app/views/checkout/steps/_payment_method.html.haml +1 -3
- data/app/views/checkout/steps/_payment_return.html.haml +6 -0
- data/app/views/checkout/steps/_shipping_method.html.haml +1 -1
- data/app/views/stall/admin_mailer/order_paid_email.html.haml +4 -0
- data/app/views/stall/carts/show.html.haml +36 -8
- data/app/views/stall/customer_mailer/order_paid_email.html.haml +8 -0
- data/app/views/stall/line_items/_added.html.haml +1 -1
- data/app/views/stall/shared/mailers/_address.html.haml +18 -0
- data/app/views/stall/shared/mailers/_cart.html.haml +114 -0
- data/config/locales/stall.fr.yml +90 -8
- data/db/migrate/20160304134849_change_all_json_columns_to_jsonb.rb +18 -0
- data/db/migrate/20160307142924_add_state_to_stall_addresses.rb +5 -0
- data/db/migrate/20160308142713_create_adjustments.rb +16 -0
- data/db/migrate/20160309165136_add_identifier_to_stall_product_lists.rb +5 -0
- data/db/migrate/20160316114649_add_data_to_stall_shipments.rb +5 -0
- data/db/migrate/20160317141632_add_state_to_stall_shipments.rb +5 -0
- data/db/migrate/20160629102943_add_active_to_stall_shipping_methods.rb +11 -0
- data/db/migrate/20160629104617_add_active_to_stall_payment_methods.rb +11 -0
- data/db/migrate/20160705110151_add_locale_to_stall_customers.rb +5 -0
- data/lib/generators/stall/checkout/step/templates/step.html.haml.erb +1 -1
- data/lib/generators/stall/checkout/step/templates/step.rb.erb +10 -2
- data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +1 -1
- data/lib/generators/stall/install/templates/initializer.rb +85 -1
- data/lib/generators/stall/model/model_generator.rb +27 -0
- data/lib/generators/stall/service/service_generator.rb +39 -0
- data/lib/generators/stall/service/templates/service.rb.erb +16 -0
- data/lib/generators/stall/shipping/calculator/calculator_generator.rb +17 -0
- data/lib/generators/stall/shipping/calculator/templates/calculator.rb.erb +29 -0
- data/lib/stall.rb +4 -0
- data/lib/stall/addressable.rb +28 -2
- data/lib/stall/cart_helper.rb +84 -0
- data/lib/stall/carts_cleaner.rb +46 -0
- data/lib/stall/checkout.rb +2 -0
- data/lib/stall/checkout/informations_checkout_step.rb +3 -5
- data/lib/stall/checkout/payment_checkout_step.rb +3 -0
- data/lib/stall/checkout/payment_return_checkout_step.rb +11 -0
- data/lib/stall/checkout/shipping_method_checkout_step.rb +2 -1
- data/lib/stall/checkout/step.rb +47 -11
- data/lib/stall/checkout/step_form.rb +71 -0
- data/lib/stall/checkout/wizard.rb +15 -5
- data/lib/stall/config.rb +51 -0
- data/lib/stall/engine.rb +24 -2
- data/lib/stall/payable.rb +26 -0
- data/lib/stall/payments.rb +7 -3
- data/lib/stall/payments/config.rb +37 -0
- data/lib/stall/payments/fake_gateway_payment_notification.rb +34 -0
- data/lib/stall/payments/gateway.rb +19 -12
- data/lib/stall/payments/urls_config.rb +40 -0
- data/lib/stall/priceable.rb +7 -0
- data/lib/stall/rails/currency_helper.rb +1 -1
- data/lib/stall/routes.rb +17 -4
- data/lib/stall/sellable.rb +1 -2
- data/lib/stall/shipping.rb +3 -1
- data/lib/stall/shipping/calculator.rb +36 -3
- data/lib/stall/shipping/config.rb +17 -3
- data/lib/stall/shipping/country_weight_table_calculator.rb +1 -1
- data/lib/stall/shipping/free_shipping_calculator.rb +1 -1
- data/lib/stall/utils.rb +2 -2
- data/lib/stall/utils/config_dsl.rb +5 -1
- data/lib/stall/version.rb +1 -1
- data/lib/tasks/stall_tasks.rake +11 -0
- metadata +73 -15
- data/app/assets/javascripts/stall/application.js +0 -13
- data/app/assets/javascripts/stall/carts.js +0 -2
- data/app/helpers/stall/cart_helper.rb +0 -28
- data/app/models/stall/address.rb +0 -5
- data/app/models/stall/address_ownership.rb +0 -8
- data/app/models/stall/cart.rb +0 -31
- data/app/models/stall/customer.rb +0 -8
- data/app/models/stall/line_item.rb +0 -49
- data/app/models/stall/payment.rb +0 -14
- data/app/models/stall/payment_method.rb +0 -7
- data/app/models/stall/product_list.rb +0 -68
- data/app/models/stall/shipment.rb +0 -15
- data/app/models/stall/shipping_method.rb +0 -5
@@ -19,15 +19,16 @@ module Stall
|
|
19
19
|
@cart = cart
|
20
20
|
end
|
21
21
|
|
22
|
-
def initialize_current_step(
|
23
|
-
step = current_step.new(cart
|
24
|
-
|
25
|
-
#
|
22
|
+
def initialize_current_step(&block)
|
23
|
+
step = current_step.new(cart)
|
24
|
+
|
25
|
+
# This block allows us to let inject controller-bound dependencies
|
26
|
+
# into the step just after it's initialized
|
26
27
|
block.call(step) if block
|
27
28
|
|
28
29
|
if step.skip?
|
29
30
|
validate_current_step!
|
30
|
-
initialize_current_step(
|
31
|
+
initialize_current_step(&block)
|
31
32
|
else
|
32
33
|
step
|
33
34
|
end
|
@@ -56,6 +57,10 @@ module Stall
|
|
56
57
|
step_index && step_index >= steps_count - 1
|
57
58
|
end
|
58
59
|
|
60
|
+
def step_complete?(step_identifier)
|
61
|
+
steps.index(cart.state) > steps.index(step_identifier.to_sym)
|
62
|
+
end
|
63
|
+
|
59
64
|
def steps_count
|
60
65
|
@steps_count ||= steps.length
|
61
66
|
end
|
@@ -65,6 +70,11 @@ module Stall
|
|
65
70
|
cart.save!
|
66
71
|
end
|
67
72
|
|
73
|
+
def move_to_step!(target_step)
|
74
|
+
cart.state = target_step
|
75
|
+
cart.save!
|
76
|
+
end
|
77
|
+
|
68
78
|
def self.route_key
|
69
79
|
name.gsub(/CheckoutWizard/, '').underscore.gsub('_', ' ').parameterize
|
70
80
|
end
|
data/lib/stall/config.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
module Stall
|
2
2
|
class Config
|
3
3
|
extend Stall::Utils::ConfigDSL
|
4
|
+
param :store_name
|
5
|
+
|
6
|
+
# Admin e-mail address to which order notifications will be sent
|
7
|
+
param :admin_email, -> { ENV['STALL_ADMIN_EMAIL'] || 'admin.change_me_in.stall.rb@example.com' }
|
8
|
+
|
9
|
+
# E-mail address used to send e-mails to customers
|
10
|
+
param :sender_email, -> { ENV['STALL_SENDER_EMAIL'] || 'shop.change_me_in.stall.rb@example.com' }
|
4
11
|
|
5
12
|
# Default VAT rate
|
6
13
|
param :vat_rate, BigDecimal.new('20.0')
|
@@ -11,9 +18,17 @@ module Stall
|
|
11
18
|
# Engine's ApplicationController parent
|
12
19
|
param :application_controller_ancestor, '::ApplicationController'
|
13
20
|
|
21
|
+
param :mailers_parent_class, 'ActionMailer::Base'
|
22
|
+
|
23
|
+
# Default layout used for the checkout
|
24
|
+
param :default_layout
|
25
|
+
|
14
26
|
# Default currency for money objects
|
15
27
|
param :default_currency, 'EUR'
|
16
28
|
|
29
|
+
# Default app domain for building routes
|
30
|
+
param :_default_app_domain
|
31
|
+
|
17
32
|
# Default checkout wizard used
|
18
33
|
param :default_checkout_wizard, 'DefaultCheckoutWizard'
|
19
34
|
|
@@ -23,10 +38,23 @@ module Stall
|
|
23
38
|
# Default step initialization hook
|
24
39
|
param :_steps_initialization_callback
|
25
40
|
|
41
|
+
param :services, {}
|
42
|
+
|
43
|
+
# Duration after which an empty cart is cleaned out by the rake task
|
44
|
+
param :empty_carts_expires_after, 1.day
|
45
|
+
|
46
|
+
# Duration after which an aborted is cleaned out by the rake task
|
47
|
+
param :aborted_carts_expires_after, 14.days
|
48
|
+
|
49
|
+
|
26
50
|
def shipping
|
27
51
|
@shipping ||= Stall::Shipping::Config.new
|
28
52
|
end
|
29
53
|
|
54
|
+
def payment
|
55
|
+
@payment ||= Stall::Payments::Config.new
|
56
|
+
end
|
57
|
+
|
30
58
|
def steps_initialization(value = nil, &block)
|
31
59
|
if (value ||= block)
|
32
60
|
@_steps_initialization_callback = value
|
@@ -34,5 +62,28 @@ module Stall
|
|
34
62
|
@_steps_initialization_callback
|
35
63
|
end
|
36
64
|
end
|
65
|
+
|
66
|
+
def default_app_domain
|
67
|
+
_default_app_domain || ENV['APP_DOMAIN']
|
68
|
+
end
|
69
|
+
|
70
|
+
# Fetch user config and add top-namespace lookup to avoid collision
|
71
|
+
# with Stall module services
|
72
|
+
#
|
73
|
+
# Default allows looking up Stall namespace automatically, when no
|
74
|
+
# config has been given
|
75
|
+
def service_for(identifier)
|
76
|
+
class_name = if (service_name = services[identifier])
|
77
|
+
"::#{ services[identifier].gsub(/^::/, '') }"
|
78
|
+
else
|
79
|
+
"Stall::#{ identifier.to_s.camelize }Service"
|
80
|
+
end
|
81
|
+
|
82
|
+
class_name.constantize
|
83
|
+
end
|
84
|
+
|
85
|
+
def services=(value)
|
86
|
+
self.services.merge!(value)
|
87
|
+
end
|
37
88
|
end
|
38
89
|
end
|
data/lib/stall/engine.rb
CHANGED
@@ -1,15 +1,37 @@
|
|
1
1
|
module Stall
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
-
initializer '
|
3
|
+
initializer 'stall.set_money_gem_default_currency' do
|
4
4
|
Money.default_currency = Stall.config.default_currency
|
5
5
|
end
|
6
6
|
|
7
|
-
initializer '
|
7
|
+
initializer 'stall.override_actionview_number_helpers' do
|
8
8
|
ActiveSupport.on_load(:action_view) do
|
9
9
|
include Stall::CurrencyHelper
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
initializer 'stall.include_cart_helper' do
|
14
|
+
ActiveSupport.on_load(:action_controller) do
|
15
|
+
include Stall::CartHelper
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer 'stall.ensure_shipping_method_for_all_calculators' do
|
20
|
+
Stall::Shipping.calculators.each_key do |name|
|
21
|
+
ShippingMethod.where(identifier: name).first_or_create do |method|
|
22
|
+
method.name = name.to_s.humanize
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer 'stall.ensure_payment_method_for_all_gateways' do
|
28
|
+
Stall::Payments.gateways.each_key do |name|
|
29
|
+
PaymentMethod.where(identifier: name).first_or_create do |method|
|
30
|
+
method.name = name.to_s.humanize
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
13
35
|
# Development : Configure rails generators to only generate the target
|
14
36
|
# files and not try to generate useless complementary files
|
15
37
|
#
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Stall
|
2
|
+
module Payable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_one :payment, dependent: :destroy,
|
7
|
+
inverse_of: :cart,
|
8
|
+
foreign_key: :cart_id
|
9
|
+
accepts_nested_attributes_for :payment
|
10
|
+
|
11
|
+
scope :paid, -> {
|
12
|
+
joins(:payment).where.not(stall_payments: { paid_at: nil })
|
13
|
+
}
|
14
|
+
|
15
|
+
scope :finalized, -> { paid }
|
16
|
+
|
17
|
+
scope :aborted, ->(options = {}) {
|
18
|
+
joins('LEFT JOIN stall_payments ON stall_payments.cart_id = stall_product_lists.id')
|
19
|
+
.where(stall_payments: { paid_at: nil })
|
20
|
+
.older_than(options.fetch(:before, 1.day.ago))
|
21
|
+
}
|
22
|
+
|
23
|
+
delegate :paid?, to: :payment, allow_nil: true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/stall/payments.rb
CHANGED
@@ -3,11 +3,15 @@ module Stall
|
|
3
3
|
extend ActiveSupport::Autoload
|
4
4
|
|
5
5
|
autoload :Gateway
|
6
|
-
|
7
|
-
|
6
|
+
autoload :Config
|
7
|
+
autoload :UrlsConfig
|
8
|
+
autoload :FakeGatewayPaymentNotification
|
8
9
|
|
9
10
|
mattr_reader :gateways
|
10
|
-
@@gateways = {}
|
11
|
+
@@gateways = {}.with_indifferent_access
|
12
|
+
|
13
|
+
mattr_reader :payment_urls
|
14
|
+
@@payment_urls = {}.with_indifferent_access
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Stall
|
2
|
+
module Payments
|
3
|
+
class Config
|
4
|
+
extend Stall::Utils::ConfigDSL
|
5
|
+
|
6
|
+
# This mehtod allows for registering an in-app gateway that can be
|
7
|
+
# auto loaded without having to explicitly require it
|
8
|
+
#
|
9
|
+
# Use a string representing the gateway name if the target class is
|
10
|
+
# auto-loaded by Rails
|
11
|
+
#
|
12
|
+
def register_gateway(name, gateway)
|
13
|
+
Stall::Payments.gateways[name] = gateway
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure
|
17
|
+
yield self
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure_urls(&block)
|
21
|
+
Stall::Payments::UrlsConfig.config_block = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(name, *args)
|
25
|
+
if (gateway = Stall::Payments::Gateway.for(name))
|
26
|
+
if block_given?
|
27
|
+
yield gateway
|
28
|
+
else
|
29
|
+
gateway
|
30
|
+
end
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Stall
|
2
|
+
module Payments
|
3
|
+
class FakeGatewayPaymentNotification
|
4
|
+
attr_reader :cart
|
5
|
+
|
6
|
+
def initialize(cart)
|
7
|
+
@cart = cart
|
8
|
+
end
|
9
|
+
|
10
|
+
def params
|
11
|
+
raise 'FakeGatewayPaymentNotification subclasses must define the ' \
|
12
|
+
'#params method to return a valid notification params hash.'
|
13
|
+
end
|
14
|
+
|
15
|
+
def raw_post
|
16
|
+
params.to_query
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def transaction_id
|
22
|
+
['FAKE', cart.reference, 0].join('-')
|
23
|
+
end
|
24
|
+
|
25
|
+
def gateway
|
26
|
+
@gateway ||= gateway_class.new(cart)
|
27
|
+
end
|
28
|
+
|
29
|
+
def gateway_class
|
30
|
+
@gateway_class ||= Stall::Payments::Gateway.for(cart.payment.payment_method)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -10,24 +10,27 @@ module Stall
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.register(name)
|
13
|
-
Stall
|
13
|
+
Stall.config.payment.register_gateway(name, self)
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def self.for(payment_method)
|
17
|
+
identifier = case payment_method
|
18
|
+
when String, Symbol then payment_method.to_s
|
19
|
+
else payment_method.identifier
|
20
|
+
end
|
21
|
+
|
22
|
+
gateway = Stall::Payments.gateways[identifier]
|
23
|
+
String === gateway ? gateway.constantize : gateway
|
21
24
|
end
|
22
25
|
|
23
|
-
def self.
|
24
|
-
|
26
|
+
def self.request(cart)
|
27
|
+
raise NoMethodError,
|
28
|
+
'Subclasses must implement the .request(cart) class method '
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
31
|
+
def self.response(_request)
|
28
32
|
raise NoMethodError,
|
29
|
-
'Subclasses must implement the
|
30
|
-
'method to handle payment verifications and cart payment validation'
|
33
|
+
'Subclasses must implement the .response(request) class method '
|
31
34
|
end
|
32
35
|
|
33
36
|
def transaction_id
|
@@ -50,10 +53,14 @@ module Stall
|
|
50
53
|
{ text: nil }
|
51
54
|
end
|
52
55
|
|
56
|
+
def payment_urls
|
57
|
+
@payment_urls ||= Stall::Payments::UrlsConfig.new(cart)
|
58
|
+
end
|
59
|
+
|
53
60
|
private
|
54
61
|
|
55
62
|
def next_transaction_id
|
56
|
-
if (last_transaction =
|
63
|
+
if (last_transaction = Payment.order("data->>'transaction_id' DESC").select(:data).first)
|
57
64
|
if (id = last_transaction.transaction_id)
|
58
65
|
index = id.split('-').pop.to_i + 1
|
59
66
|
return transaction_id_for(index)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Stall
|
2
|
+
module Payments
|
3
|
+
class UrlsConfig
|
4
|
+
include Rails.application.routes.url_helpers
|
5
|
+
|
6
|
+
class_attribute :config_block
|
7
|
+
|
8
|
+
attr_reader :cart
|
9
|
+
|
10
|
+
attr_accessor :payment_notification_url,
|
11
|
+
:payment_success_return_url,
|
12
|
+
:payment_failure_return_url
|
13
|
+
|
14
|
+
def initialize(cart)
|
15
|
+
@cart = cart
|
16
|
+
|
17
|
+
# Parse URLs
|
18
|
+
instance_exec(self, &config_block)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def config_block
|
24
|
+
self.class.config_block || default_config
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_config
|
28
|
+
->(urls) {
|
29
|
+
urls.payment_notification_url = notify_payment_url(gateway: gateway_identifier, host: Stall.config.default_app_domain)
|
30
|
+
urls.payment_success_return_url = process_checkout_step_url(cart.identifier, host: Stall.config.default_app_domain)
|
31
|
+
urls.payment_failure_return_url = process_checkout_step_url(cart.identifier, host: Stall.config.default_app_domain)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def gateway_identifier
|
36
|
+
@gateway_identifier ||= cart.payment.payment_method.identifier
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/stall/routes.rb
CHANGED
@@ -13,13 +13,26 @@ module Stall
|
|
13
13
|
resources :line_items
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
get 'checkout/:cart_key' => 'checkouts#show', as: :checkout
|
17
17
|
|
18
|
-
scope '
|
19
|
-
|
18
|
+
scope 'checkout', module: 'checkout', as: :checkout do
|
19
|
+
scope '(:cart_key)' do
|
20
|
+
resource :step, only: [:show, :update] do
|
21
|
+
post '/', action: :update, as: :update
|
22
|
+
get '/process', action: :update, as: :process
|
23
|
+
get 'change/:step', action: :change, as: :change
|
24
|
+
end
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
|
-
|
28
|
+
|
29
|
+
scope '/:gateway' do
|
30
|
+
resource :payment, only: [] do
|
31
|
+
member do
|
32
|
+
match 'notify', action: 'notify', via: [:get, :post]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
23
36
|
end
|
24
37
|
end
|
25
38
|
end
|
data/lib/stall/sellable.rb
CHANGED