stall 0.1.3 → 0.2.0
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 +41 -3
- data/app/assets/javascripts/stall/add-to-cart-form.coffee +55 -3
- data/app/assets/javascripts/stall/addresses-fields.coffee +17 -0
- data/app/assets/javascripts/stall/remote-sign-in-form.coffee +55 -0
- data/app/assets/javascripts/stall.coffee +3 -1
- data/app/controllers/stall/application_controller.rb +1 -1
- data/app/controllers/stall/carts_controller.rb +1 -3
- data/app/controllers/stall/checkout/steps_controller.rb +18 -1
- data/app/helpers/stall/checkout_helper.rb +7 -8
- data/app/helpers/stall/customers_helper.rb +25 -0
- data/app/models/stall/models/cart.rb +2 -0
- data/app/models/stall/models/customer.rb +20 -0
- data/app/models/stall/models/product_list.rb +13 -0
- data/app/views/checkout/steps/_informations.html.haml +24 -35
- data/app/views/checkout/steps/_payment.html.haml +1 -45
- data/app/views/checkout/steps/_shipping_method.html.haml +0 -1
- data/app/views/stall/addresses/_fields.html.haml +16 -0
- data/app/views/stall/addresses/_nested_fields.html.haml +14 -0
- data/app/views/stall/carts/_cart.html.haml +45 -0
- data/app/views/stall/carts/show.html.haml +2 -2
- data/app/views/stall/customers/_fields.html.haml +32 -0
- data/app/views/stall/customers/_sign_in.html.haml +20 -0
- data/app/views/stall/line_items/_form.html.haml +2 -2
- data/app/views/stall/payments/_fields.html.haml +2 -0
- data/app/views/stall/shared/mailers/_cart.html.haml +31 -29
- data/app/views/stall/shipments/_fields.html.haml +2 -0
- data/config/locales/stall.fr.yml +35 -8
- data/lib/generators/stall/checkout/step/templates/step.rb.erb +9 -0
- data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +1 -1
- data/lib/generators/stall/install/install_generator.rb +6 -0
- data/lib/generators/stall/install/templates/initializer.rb +10 -1
- data/lib/generators/stall/view/view_generator.rb +38 -0
- data/lib/stall/addressable.rb +2 -0
- data/lib/stall/addresses/copier_base.rb +24 -0
- data/lib/stall/addresses/copy_source_to_target.rb +45 -0
- data/lib/stall/addresses/prefill_target_from_source.rb +29 -0
- data/lib/stall/addresses.rb +9 -0
- data/lib/stall/cart_helper.rb +14 -1
- data/lib/stall/checkout/informations_checkout_step.rb +133 -14
- data/lib/stall/checkout/payment_checkout_step.rb +21 -1
- data/lib/stall/checkout/step.rb +22 -15
- data/lib/stall/checkout/step_form.rb +22 -8
- data/lib/stall/config.rb +17 -3
- data/lib/stall/engine.rb +6 -0
- data/lib/stall/payable.rb +9 -0
- data/lib/stall/payments/gateway.rb +16 -6
- data/lib/stall/payments/urls_config.rb +2 -2
- data/lib/stall/rails/routing_mapper.rb +4 -6
- data/lib/stall/routes.rb +4 -1
- data/lib/stall/sellable.rb +13 -3
- data/lib/stall/version.rb +1 -1
- data/lib/stall.rb +1 -0
- metadata +25 -6
- data/lib/stall/checkout/payment_method_checkout_step.rb +0 -9
- data/lib/stall/checkout/shipping_method_checkout_step.rb +0 -21
@@ -1,20 +1,65 @@
|
|
1
1
|
module Stall
|
2
2
|
module Checkout
|
3
3
|
class InformationsCheckoutStep < Stall::Checkout::Step
|
4
|
+
validations do
|
5
|
+
validate :customer_accepts_terms
|
6
|
+
validates :customer, :shipping_address, presence: true
|
7
|
+
|
8
|
+
validates :billing_address, presence: true,
|
9
|
+
if: :use_another_address_for_billing?
|
10
|
+
|
11
|
+
nested :shipping_address do
|
12
|
+
validates :civility, :first_name, :last_name, :address, :country,
|
13
|
+
:zip, :city, presence: true
|
14
|
+
end
|
15
|
+
|
16
|
+
nested :billing_address do
|
17
|
+
validates :civility, :first_name, :last_name, :address, :country,
|
18
|
+
:zip, :city, presence: true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
4
22
|
def prepare
|
5
23
|
ensure_customer
|
6
|
-
|
7
|
-
|
24
|
+
prefill_addresses_from_customer
|
25
|
+
ensure_shipment
|
26
|
+
ensure_payment
|
8
27
|
end
|
9
28
|
|
10
29
|
def process
|
30
|
+
prepare_user_attributes
|
11
31
|
cart.assign_attributes(cart_params)
|
12
32
|
process_addresses
|
13
|
-
|
33
|
+
|
34
|
+
return unless valid?
|
35
|
+
|
36
|
+
cart.save.tap do |valid|
|
37
|
+
assign_addresses_to_customer!
|
38
|
+
calculate_shipping_fee!
|
39
|
+
end
|
14
40
|
end
|
15
41
|
|
16
42
|
private
|
17
43
|
|
44
|
+
def cart_params
|
45
|
+
@cart_params ||= params.require(:cart).permit(
|
46
|
+
:use_another_address_for_billing, :terms,
|
47
|
+
:payment_method_id, :shipping_method_id,
|
48
|
+
customer_attributes: [
|
49
|
+
:email, user_attributes: [
|
50
|
+
:password, :password_confirmation
|
51
|
+
]
|
52
|
+
],
|
53
|
+
address_ownerships_attributes: [
|
54
|
+
:id, :shipping, :billing,
|
55
|
+
address_attributes: [
|
56
|
+
:id, :civility, :first_name, :last_name, :address,
|
57
|
+
:address_details, :country, :zip, :city, :phone
|
58
|
+
]
|
59
|
+
]
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
18
63
|
def ensure_customer
|
19
64
|
cart.build_customer unless cart.customer
|
20
65
|
end
|
@@ -24,18 +69,92 @@ module Stall
|
|
24
69
|
ownership.address || ownership.build_address
|
25
70
|
end
|
26
71
|
|
27
|
-
def
|
28
|
-
unless
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
72
|
+
def ensure_billing_address
|
73
|
+
return ensure_address(:billing) unless cart_params[:use_another_address_for_billing] == '0'
|
74
|
+
|
75
|
+
# If the form was submitted and we merged both shipping and billing
|
76
|
+
# addresses together, we must separate them back to present them to
|
77
|
+
# the user form, avoiding to render errors in a form that the user
|
78
|
+
# didn't even see before
|
79
|
+
ownership = cart.address_ownership_for(:billing)
|
80
|
+
ownership.billing = false if ownership.shipping
|
81
|
+
ensure_address(:billing)
|
82
|
+
end
|
83
|
+
|
84
|
+
def ensure_shipment
|
85
|
+
cart.build_shipment unless cart.shipment
|
86
|
+
end
|
87
|
+
|
88
|
+
def ensure_payment
|
89
|
+
cart.build_payment unless cart.payment
|
90
|
+
end
|
91
|
+
|
92
|
+
# Remvove user attributes when no account should be created, for an
|
93
|
+
# "anonymous" order creation.
|
94
|
+
#
|
95
|
+
def prepare_user_attributes
|
96
|
+
return if params[:create_account] == '1'
|
97
|
+
|
98
|
+
if cart_params[:customer_attributes] && cart_params[:customer_attributes][:user_attributes]
|
99
|
+
cart_params[:customer_attributes].delete(:user_attributes)
|
38
100
|
end
|
101
|
+
|
102
|
+
# Remove user from customer to avoid automatic validation of the user
|
103
|
+
# if no user should be saved with the customer
|
104
|
+
unless stall_user_signed_in? || cart.customer.try(:user).try(:persisted?) ||
|
105
|
+
!cart.customer
|
106
|
+
then
|
107
|
+
cart.customer.user = nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Merges shipping and billing addresses into one address when the visitor
|
112
|
+
# has chosen to use the shipping address for both.
|
113
|
+
#
|
114
|
+
def process_addresses
|
115
|
+
return if use_another_address_for_billing?
|
116
|
+
|
117
|
+
shipping_ownership = cart.address_ownership_for(:shipping)
|
118
|
+
billing_ownership = cart.address_ownership_for(:billing)
|
119
|
+
|
120
|
+
return if billing_ownership == shipping_ownership
|
121
|
+
|
122
|
+
# If the user choosed to receive his order by shipping and that he
|
123
|
+
# choosed not to fill a billing address, we remove the billing address
|
124
|
+
# hidden form that was submitted in the step, and make the shipping
|
125
|
+
# address be used as billing address too
|
126
|
+
cart.address_ownerships.destroy(billing_ownership) if billing_ownership
|
127
|
+
cart.mark_address_ownership_as_billing(shipping_ownership) if shipping_ownership
|
128
|
+
end
|
129
|
+
|
130
|
+
# Assigns the shipping fees to the cart based on the selected shipping
|
131
|
+
# method
|
132
|
+
#
|
133
|
+
def calculate_shipping_fee!
|
134
|
+
service_class = Stall.config.service_for(:shipping_fee_calculator)
|
135
|
+
service_class.new(cart).call
|
136
|
+
end
|
137
|
+
|
138
|
+
# Fetches addresses from the customer account and copy them to the
|
139
|
+
# cart to pre-fill the fields for the user
|
140
|
+
#
|
141
|
+
def prefill_addresses_from_customer
|
142
|
+
Stall::Addresses::PrefillTargetFromSource.new(cart.customer, cart).copy
|
143
|
+
end
|
144
|
+
|
145
|
+
# Copies the addresses filled in the cart to the customer account for
|
146
|
+
# next orders informations pre-filling
|
147
|
+
#
|
148
|
+
def assign_addresses_to_customer!
|
149
|
+
Stall::Addresses::CopySourceToTarget.new(cart, cart.customer).copy!
|
150
|
+
end
|
151
|
+
|
152
|
+
def use_another_address_for_billing?
|
153
|
+
@use_another_address_for_billing ||= cart_params[:use_another_address_for_billing] == '1'
|
154
|
+
end
|
155
|
+
|
156
|
+
def customer_accepts_terms
|
157
|
+
cart.errors.add(:terms, :accepted) unless cart.terms == '1'
|
39
158
|
end
|
40
159
|
end
|
41
160
|
end
|
@@ -1,8 +1,22 @@
|
|
1
1
|
module Stall
|
2
2
|
module Checkout
|
3
3
|
class PaymentCheckoutStep < Stall::Checkout::Step
|
4
|
+
# Determine wether the customer's payment has been validated or not.
|
5
|
+
#
|
6
|
+
# By default, the payment processing occurs in the background and, for
|
7
|
+
# some of the payment gateways, can be run asynchronously. In this case,
|
8
|
+
# the gateway should redirect here with the `:succeeded` param in the URL.
|
9
|
+
#
|
10
|
+
# If the payment processing occurs synchronously, the gateway overrides
|
11
|
+
# the #synchronous_payment_notification? method, using the cart payment
|
12
|
+
# state to determine this parameter.
|
13
|
+
#
|
4
14
|
def process
|
5
|
-
|
15
|
+
if gateway.synchronous_payment_notification?
|
16
|
+
cart.paid?
|
17
|
+
elsif params[:succeeded]
|
18
|
+
true
|
19
|
+
end
|
6
20
|
end
|
7
21
|
|
8
22
|
# When we access this step after a payment to validate the step, the cart
|
@@ -11,6 +25,12 @@ module Stall
|
|
11
25
|
def allow_inactive_carts?
|
12
26
|
!!params[:succeeded]
|
13
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def gateway
|
32
|
+
@gateway ||= Stall::Payments::Gateway.for(cart.payment.payment_method).new(cart)
|
33
|
+
end
|
14
34
|
end
|
15
35
|
end
|
16
36
|
end
|
data/lib/stall/checkout/step.rb
CHANGED
@@ -3,6 +3,8 @@ module Stall
|
|
3
3
|
class StepNotFoundError < StandardError; end
|
4
4
|
|
5
5
|
class Step
|
6
|
+
class_attribute :_validations
|
7
|
+
|
6
8
|
attr_reader :cart
|
7
9
|
|
8
10
|
def initialize(cart)
|
@@ -27,14 +29,6 @@ module Stall
|
|
27
29
|
save
|
28
30
|
end
|
29
31
|
|
30
|
-
def cart_params
|
31
|
-
@cart_params ||= if params[:cart]
|
32
|
-
params.require(:cart).permit!
|
33
|
-
else
|
34
|
-
{}.with_indifferent_access
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
32
|
def skip?
|
39
33
|
false
|
40
34
|
end
|
@@ -62,6 +56,16 @@ module Stall
|
|
62
56
|
false
|
63
57
|
end
|
64
58
|
|
59
|
+
# Run cart validations then step validations, and cart validations, returning wether they're both valid or
|
60
|
+
# not, allowing to display all involved errors to the visitor in one time
|
61
|
+
#
|
62
|
+
def valid?
|
63
|
+
cart.validate
|
64
|
+
run_step_validations!(clear: false)
|
65
|
+
|
66
|
+
cart.errors.empty?
|
67
|
+
end
|
68
|
+
|
65
69
|
# Abstracts the simple case of assigning the submitted parameters to the
|
66
70
|
# cart object, running the step validations and saving the cart
|
67
71
|
def save
|
@@ -69,6 +73,14 @@ module Stall
|
|
69
73
|
cart.save if valid?
|
70
74
|
end
|
71
75
|
|
76
|
+
private
|
77
|
+
|
78
|
+
def run_step_validations!(clear: true)
|
79
|
+
if (validations = self.class.validations)
|
80
|
+
validations.new(cart, self, clear: clear).validate
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
72
84
|
# Handles conversion from an identifier to a checkout step class, allowing
|
73
85
|
# us to specify a list of symbols in our wizard's .step macro
|
74
86
|
#
|
@@ -92,13 +104,8 @@ module Stall
|
|
92
104
|
end
|
93
105
|
|
94
106
|
def self.validations(&block)
|
95
|
-
return
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
def valid?
|
100
|
-
return true unless (validations = self.class.validations)
|
101
|
-
validations.new(cart, self).validate
|
107
|
+
return _validations unless block
|
108
|
+
self._validations = Stall::Checkout::StepForm.build(&block)
|
102
109
|
end
|
103
110
|
end
|
104
111
|
end
|
@@ -5,17 +5,28 @@ module Stall
|
|
5
5
|
|
6
6
|
class_attribute :nested_forms
|
7
7
|
|
8
|
-
attr_reader :object, :step
|
8
|
+
attr_reader :object, :step, :clear_cart_errors_before_validation
|
9
9
|
|
10
10
|
delegate :errors, to: :object
|
11
11
|
|
12
|
-
def initialize(object, step)
|
12
|
+
def initialize(object, step, clear: true)
|
13
13
|
@object = object
|
14
14
|
@step = step
|
15
|
+
@clear_cart_errors_before_validation = clear
|
15
16
|
end
|
16
17
|
|
18
|
+
# Runs form and nested forms validations and returns wether they all
|
19
|
+
# passed or not
|
20
|
+
#
|
21
|
+
# Only clear validation errors on the cart if needed, allowing to run
|
22
|
+
# cart validations before the step ones, passing clear: false in the
|
23
|
+
# form constructor, aggregating both validation sources' errors
|
24
|
+
#
|
17
25
|
def validate
|
18
|
-
|
26
|
+
errors.clear if clear_cart_errors_before_validation
|
27
|
+
run_validations!
|
28
|
+
validate_nested_forms
|
29
|
+
!errors.any?
|
19
30
|
end
|
20
31
|
|
21
32
|
def self.nested(type, &block)
|
@@ -23,6 +34,9 @@ module Stall
|
|
23
34
|
nested_forms[type] = build(&block)
|
24
35
|
end
|
25
36
|
|
37
|
+
# Build an dynamic StepForm subclass with the given block as the body
|
38
|
+
# of the class
|
39
|
+
#
|
26
40
|
def self.build(&block)
|
27
41
|
Class.new(StepForm, &block)
|
28
42
|
end
|
@@ -40,7 +54,7 @@ module Stall
|
|
40
54
|
# Override model name instanciation to add a name, since the form classes
|
41
55
|
# are anonymous, and ActiveModel::Name does not support unnamed classes
|
42
56
|
def model_name
|
43
|
-
@model_name ||= ActiveModel::Name.new(self, nil,
|
57
|
+
@model_name ||= ActiveModel::Name.new(self, nil, self.class.name)
|
44
58
|
end
|
45
59
|
|
46
60
|
private
|
@@ -54,12 +68,12 @@ module Stall
|
|
54
68
|
def validate_nested_forms
|
55
69
|
# If no nested forms are present in the class, just return true since
|
56
70
|
# no validation should be tested
|
57
|
-
return true unless
|
71
|
+
return true unless nested_forms
|
58
72
|
|
59
73
|
# Run all validations on all nested forms and ensure they're all valid
|
60
|
-
|
61
|
-
if object.respond_to?(name) && (
|
62
|
-
Array.wrap(
|
74
|
+
nested_forms.map do |name, form|
|
75
|
+
if object.respond_to?(name) && (resource = object.send(name))
|
76
|
+
Array.wrap(resource).map { |m| form.new(m, step).validate }.all?
|
63
77
|
else
|
64
78
|
# Nested validations shouldn't be run on undefined relations
|
65
79
|
true
|
data/lib/stall/config.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Stall
|
2
2
|
class Config
|
3
3
|
extend Stall::Utils::ConfigDSL
|
4
|
+
# Store name used in e-mails and other interfaces duisplaying such an
|
5
|
+
# information
|
4
6
|
param :store_name
|
5
7
|
|
6
8
|
# Admin e-mail address to which order notifications will be sent
|
@@ -9,6 +11,9 @@ module Stall
|
|
9
11
|
# E-mail address used to send e-mails to customers
|
10
12
|
param :sender_email, -> { ENV['STALL_SENDER_EMAIL'] || 'shop.change_me_in.stall.rb@example.com' }
|
11
13
|
|
14
|
+
# Email regex validation. Taken from Devise
|
15
|
+
param :email_regexp, -> { (defined?(Devise) && Devise.email_regexp) || /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/ }
|
16
|
+
|
12
17
|
# Default VAT rate
|
13
18
|
param :vat_rate, BigDecimal.new('20.0')
|
14
19
|
|
@@ -27,7 +32,7 @@ module Stall
|
|
27
32
|
param :default_currency, 'EUR'
|
28
33
|
|
29
34
|
# Default app domain for building routes
|
30
|
-
param :
|
35
|
+
param :default_app_domain
|
31
36
|
|
32
37
|
# Default checkout wizard used
|
33
38
|
param :default_checkout_wizard, 'DefaultCheckoutWizard'
|
@@ -43,9 +48,14 @@ module Stall
|
|
43
48
|
# Duration after which an empty cart is cleaned out by the rake task
|
44
49
|
param :empty_carts_expires_after, 1.day
|
45
50
|
|
46
|
-
# Duration after which an aborted is cleaned out by the rake task
|
51
|
+
# Duration after which an aborted cart is cleaned out by the rake task
|
47
52
|
param :aborted_carts_expires_after, 14.days
|
48
53
|
|
54
|
+
param :default_user_model_name, 'User'
|
55
|
+
param :default_user_helper_method, :current_user
|
56
|
+
|
57
|
+
# Configure the terms of service page path
|
58
|
+
param :terms_path, 'about:blank'
|
49
59
|
|
50
60
|
def shipping
|
51
61
|
@shipping ||= Stall::Shipping::Config.new
|
@@ -64,7 +74,7 @@ module Stall
|
|
64
74
|
end
|
65
75
|
|
66
76
|
def default_app_domain
|
67
|
-
|
77
|
+
@default_app_domain || ENV['APP_DOMAIN']
|
68
78
|
end
|
69
79
|
|
70
80
|
# Fetch user config and add top-namespace lookup to avoid collision
|
@@ -85,5 +95,9 @@ module Stall
|
|
85
95
|
def services=(value)
|
86
96
|
self.services.merge!(value)
|
87
97
|
end
|
98
|
+
|
99
|
+
def default_user_model
|
100
|
+
default_user_model_name.try(:constantize)
|
101
|
+
end
|
88
102
|
end
|
89
103
|
end
|
data/lib/stall/engine.rb
CHANGED
@@ -4,6 +4,12 @@ module Stall
|
|
4
4
|
Money.default_currency = Stall.config.default_currency
|
5
5
|
end
|
6
6
|
|
7
|
+
initializer 'stall.add_routing_mapper_extension' do
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
ActionDispatch::Routing::Mapper.send(:include, Stall::RoutingMapper)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
initializer 'stall.override_actionview_number_helpers' do
|
8
14
|
ActiveSupport.on_load(:action_view) do
|
9
15
|
include Stall::CurrencyHelper
|
data/lib/stall/payable.rb
CHANGED
@@ -22,5 +22,14 @@ module Stall
|
|
22
22
|
|
23
23
|
delegate :paid?, to: :payment, allow_nil: true
|
24
24
|
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def find_by_payment_transaction_id(transaction_id)
|
28
|
+
joins(:payment).where(
|
29
|
+
"stall_payments.data->>'transaction_id' = ?",
|
30
|
+
transaction_id
|
31
|
+
).first
|
32
|
+
end
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Stall
|
2
2
|
module Payments
|
3
3
|
class Gateway
|
4
|
-
TRANSACTION_ID_FORMAT = 'ESHOP-%{cart_id}-%{transaction_index}'
|
5
|
-
|
6
4
|
attr_reader :cart
|
7
5
|
|
8
6
|
def initialize(cart)
|
@@ -33,9 +31,9 @@ module Stall
|
|
33
31
|
'Subclasses must implement the .response(request) class method '
|
34
32
|
end
|
35
33
|
|
36
|
-
def transaction_id
|
34
|
+
def transaction_id(refresh: false)
|
37
35
|
@transaction_id ||= begin
|
38
|
-
|
36
|
+
if refresh || !(id = cart.payment.transaction_id)
|
39
37
|
id = next_transaction_id
|
40
38
|
cart.payment.update_attributes(transaction_id: id)
|
41
39
|
end
|
@@ -50,13 +48,21 @@ module Stall
|
|
50
48
|
# Most of the gateways expect some specific return, so this is to be
|
51
49
|
# overriden by subclasses
|
52
50
|
def rendering_options
|
53
|
-
{
|
51
|
+
{ nothing: false }
|
54
52
|
end
|
55
53
|
|
56
54
|
def payment_urls
|
57
55
|
@payment_urls ||= Stall::Payments::UrlsConfig.new(cart)
|
58
56
|
end
|
59
57
|
|
58
|
+
# Override this method and retrun true if the gateway payment
|
59
|
+
# notification should be taken into account to determine wether the
|
60
|
+
# payment has been successful or not when returning from the gateway.
|
61
|
+
#
|
62
|
+
def synchronous_payment_notification?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
60
66
|
private
|
61
67
|
|
62
68
|
def next_transaction_id
|
@@ -71,10 +77,14 @@ module Stall
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def transaction_id_for(index)
|
74
|
-
|
80
|
+
transaction_id_format
|
75
81
|
.gsub('%{cart_id}', cart.reference)
|
76
82
|
.gsub('%{transaction_index}', ('%05d' % index))
|
77
83
|
end
|
84
|
+
|
85
|
+
def transaction_id_format
|
86
|
+
'ESHOP-%{cart_id}-%{transaction_index}'
|
87
|
+
end
|
78
88
|
end
|
79
89
|
end
|
80
90
|
end
|
@@ -27,8 +27,8 @@ module Stall
|
|
27
27
|
def default_config
|
28
28
|
->(urls) {
|
29
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)
|
30
|
+
urls.payment_success_return_url = process_checkout_step_url(cart.identifier, host: Stall.config.default_app_domain, succeeded: true)
|
31
|
+
urls.payment_failure_return_url = process_checkout_step_url(cart.identifier, host: Stall.config.default_app_domain, aborted: true)
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# Routing mapper override to allow mounting the engine as non-isolated, avoiding
|
2
2
|
# issues with routes in templates when switching from the app to the engine
|
3
3
|
#
|
4
|
-
module
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
Stall::Routes.new(self).draw(mount_location)
|
9
|
-
end
|
4
|
+
module Stall
|
5
|
+
module RoutingMapper
|
6
|
+
def mount_stall(mount_location)
|
7
|
+
Stall::Routes.new(self).draw(mount_location)
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
data/lib/stall/routes.rb
CHANGED
@@ -19,7 +19,10 @@ module Stall
|
|
19
19
|
scope '(:cart_key)' do
|
20
20
|
resource :step, only: [:show, :update] do
|
21
21
|
post '/', action: :update, as: :update
|
22
|
-
get
|
22
|
+
get '/process', action: :update, as: :process
|
23
|
+
# Allow external URLs process steps, allowing some payment
|
24
|
+
# gateways to return the user through a POST request
|
25
|
+
post '/process', action: :foreign_update
|
23
26
|
get 'change/:step', action: :change, as: :change
|
24
27
|
end
|
25
28
|
end
|
data/lib/stall/sellable.rb
CHANGED
@@ -10,8 +10,8 @@ module Stall
|
|
10
10
|
|
11
11
|
def to_line_item
|
12
12
|
line_items.build(
|
13
|
-
name: (
|
14
|
-
unit_price: (
|
13
|
+
name: (try(:name) || try(:title)),
|
14
|
+
unit_price: try(:price),
|
15
15
|
unit_eot_price: eot_price,
|
16
16
|
vat_rate: vat_rate,
|
17
17
|
)
|
@@ -21,16 +21,26 @@ module Stall
|
|
21
21
|
(vat_rate / 100.0) + 1
|
22
22
|
end
|
23
23
|
|
24
|
+
def currency
|
25
|
+
Money.default_currency
|
26
|
+
end
|
27
|
+
|
24
28
|
private
|
25
29
|
|
26
30
|
def default_eot_price
|
27
|
-
price
|
31
|
+
if (price = try(:price))
|
32
|
+
price / vat_ratio
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
def default_vat_rate
|
31
37
|
@default_vat_rate ||= Stall.config.vat_rate
|
32
38
|
end
|
33
39
|
|
40
|
+
# Create default handlers for the `#eot_price` and `#vat_rate` methods that
|
41
|
+
# don't need to be explictly defined if the whole shop has a single VAT rate
|
42
|
+
# for all products
|
43
|
+
#
|
34
44
|
def method_missing(name, *args, &block)
|
35
45
|
if [:eot_price, :vat_rate].include?(name)
|
36
46
|
send(:"default_#{ name }")
|
data/lib/stall/version.rb
CHANGED