stall 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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