stall 0.1.0 → 0.1.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 +4 -4
- data/README.md +63 -0
- data/app/controllers/stall/checkout/steps_controller.rb +6 -1
- data/app/controllers/stall/payments_controller.rb +9 -0
- data/app/helpers/stall/cart_helper.rb +2 -2
- data/app/models/stall/address_ownership.rb +0 -7
- data/app/models/stall/cart.rb +17 -0
- data/app/models/stall/customer.rb +3 -0
- data/app/models/stall/line_item.rb +4 -0
- data/app/models/stall/payment.rb +14 -0
- data/app/models/stall/payment_method.rb +7 -0
- data/app/models/stall/product_list.rb +12 -0
- data/app/models/stall/shipment.rb +1 -1
- data/app/services/stall/payment_notification_service.rb +31 -0
- data/app/views/checkout/steps/_payment.html.haml +28 -0
- data/app/views/checkout/steps/_payment_method.html.haml +10 -0
- data/app/views/stall/carts/show.html.haml +12 -0
- data/app/views/stall/line_items/_form.html.haml +1 -1
- data/config/locales/stall.fr.yml +9 -0
- data/db/migrate/20160118121116_create_stall_product_lists.rb +1 -0
- data/db/migrate/20160125100733_create_stall_payment_methods.rb +10 -0
- data/db/migrate/20160125100734_create_stall_payments.rb +15 -0
- data/db/migrate/20160127113619_add_user_to_customer.rb +5 -0
- data/lib/generators/stall/checkout/step/step_generator.rb +9 -1
- data/lib/generators/stall/checkout/step/templates/step.html.haml.erb +7 -0
- data/lib/generators/stall/checkout/step/templates/step.rb.erb +17 -0
- data/lib/generators/stall/checkout/wizard/wizard_generator.rb +1 -1
- data/lib/generators/stall/install/install_generator.rb +1 -1
- data/lib/generators/stall/install/templates/initializer.rb +10 -0
- data/lib/stall.rb +1 -0
- data/lib/stall/checkout/informations_checkout_step.rb +7 -4
- data/lib/stall/checkout/payment_method_checkout_step.rb +3 -0
- data/lib/stall/checkout/step.rb +11 -1
- data/lib/stall/checkout/wizard.rb +15 -3
- data/lib/stall/config.rb +11 -0
- data/lib/stall/engine.rb +0 -6
- data/lib/stall/payments.rb +13 -0
- data/lib/stall/payments/gateway.rb +73 -0
- data/lib/stall/routes.rb +10 -10
- data/lib/stall/sellable.rb +38 -3
- data/lib/stall/shipping/calculator.rb +1 -1
- data/lib/stall/utils.rb +1 -1
- data/lib/stall/version.rb +1 -1
- metadata +27 -5
- data/app/models/stall.rb +0 -5
- data/lib/stall/sellable/mixin.rb +0 -17
- data/lib/stall/sellable/model.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8be8383152aa8e6949b5ff132d32521541d15625
|
4
|
+
data.tar.gz: b62bad7b2c986d09c579bee22d959c4984dda358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3931aee7e76b2e07d36b8f9f62e2e7fa89688195da6fd3fea8c5d1f0bd57f7b3e92343cea51ea4e734700dc0f81ba160c7117ed1fa1ce6928802d917cf7b26f6
|
7
|
+
data.tar.gz: dd6a569d77cdd85bad7832ffe7aa9dbc21e6fd6450d4ec23e8399063c8c4c688147566cfec61faa5e19aa8977864d3e0fe22c858fb22a3518bb3e6689da7402c
|
data/README.md
CHANGED
@@ -1,6 +1,69 @@
|
|
1
1
|
# Stall
|
2
2
|
|
3
|
+
[](http://travis-ci.org/rails-stall/stall)
|
4
|
+
[](https://codeclimate.com/github/rails-stall/stall)
|
3
5
|
|
6
|
+
Stall is a flexible e-commerce framework for Rails with some specific concerns
|
7
|
+
in mind :
|
8
|
+
|
9
|
+
- Product models and categorization handling is flexible and done in the app
|
10
|
+
- The checkout process is easily configurable, overridable but has a standard working default
|
11
|
+
- The whole system is modular and extensible
|
12
|
+
- The core should stay small and vendor-specific code (payment gateways, shipping carriers) is done in separate gems
|
13
|
+
- Authentication and admin is better handled by existing gems
|
14
|
+
|
15
|
+
**Note :** This gem is under active development, and is a complete rewrite of
|
16
|
+
the [glysellin](https://github.com/glysellin/glysellin) gem, but with a more
|
17
|
+
flexible structure, and with a complete test coverage
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add to your Gemfile and `bundle install` :
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'stall'
|
25
|
+
```
|
26
|
+
|
27
|
+
Then run the install generator :
|
28
|
+
|
29
|
+
```bash
|
30
|
+
rails generate stall:install
|
31
|
+
```
|
32
|
+
|
33
|
+
This will generate :
|
34
|
+
|
35
|
+
- The stall models migrations
|
36
|
+
- A default initializer file to configure the system
|
37
|
+
- A default checkout wizard class making it easy to configure your checkout process
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
### Making a model sellable
|
42
|
+
|
43
|
+
Stall allows you to make any model sellable by including the `Stall::Sellable`
|
44
|
+
mixin into your model :
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Book < ActiveRecord::Base
|
48
|
+
include Stall::Sellable
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
You can now add the "Add to cart" button to your templates :
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
= add_to_cart_form_for(@book)
|
56
|
+
```
|
57
|
+
|
58
|
+
For more informations see the Wiki page :
|
59
|
+
[Allowing customers to add products to cart](https://github.com/rails-stall/stall/wiki/Allowing-customers-to-add-products-to-cart)
|
60
|
+
|
61
|
+
### Configuring the checkout flow
|
62
|
+
|
63
|
+
The checkout process is completely flexible and can be overriden easily.
|
64
|
+
|
65
|
+
Please see the Wiki page :
|
66
|
+
[The checkout process](https://github.com/rails-stall/stall/wiki/The-checkout-process)
|
4
67
|
|
5
68
|
# Licence
|
6
69
|
|
@@ -25,7 +25,12 @@ module Stall
|
|
25
25
|
def load_step
|
26
26
|
@cart = Stall::Cart.find_by_token(params[:cart_id])
|
27
27
|
@wizard = @cart.wizard.new(@cart)
|
28
|
-
|
28
|
+
|
29
|
+
@step = @wizard.initialize_current_step(params) do |step|
|
30
|
+
if Stall.config.steps_initialization
|
31
|
+
instance_exec(step, &Stall.config.steps_initialization)
|
32
|
+
end
|
33
|
+
end
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|
@@ -8,8 +8,8 @@ module Stall
|
|
8
8
|
|
9
9
|
def load_current_cart
|
10
10
|
if (cart_token = session[cart_store_key_for(:default)])
|
11
|
-
if (
|
12
|
-
return
|
11
|
+
if (current_cart = Stall::Cart.find_by_token(cart_token))
|
12
|
+
return current_cart
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/app/models/stall/cart.rb
CHANGED
@@ -2,13 +2,30 @@ module Stall
|
|
2
2
|
class Cart < Stall::ProductList
|
3
3
|
include Stall::Addressable
|
4
4
|
|
5
|
+
store_accessor :data, :reference
|
6
|
+
|
5
7
|
has_one :shipment, dependent: :destroy, inverse_of: :cart
|
6
8
|
accepts_nested_attributes_for :shipment
|
7
9
|
|
10
|
+
has_one :payment, dependent: :destroy, inverse_of: :cart
|
11
|
+
accepts_nested_attributes_for :payment
|
12
|
+
|
13
|
+
after_save :ensure_reference, on: :create
|
14
|
+
|
8
15
|
def total_weight
|
9
16
|
line_items.reduce(0) do |total, line_item|
|
10
17
|
total + (line_item.weight || Stall.config.default_product_weight)
|
11
18
|
end
|
12
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def ensure_reference
|
24
|
+
unless reference.present?
|
25
|
+
reference = [Time.now.strftime('%Y%m%d'), ('%05d' % id)].join('-')
|
26
|
+
self.reference = reference
|
27
|
+
save(validate: false)
|
28
|
+
end
|
29
|
+
end
|
13
30
|
end
|
14
31
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Stall
|
2
|
+
class Payment < ActiveRecord::Base
|
3
|
+
store_accessor :data, :transaction_id
|
4
|
+
|
5
|
+
belongs_to :payment_method
|
6
|
+
belongs_to :cart
|
7
|
+
|
8
|
+
validates :cart, :payment_method, presence: true
|
9
|
+
|
10
|
+
def pay!
|
11
|
+
update_attributes!(paid_at: Time.now)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -25,6 +25,18 @@ module Stall
|
|
25
25
|
token
|
26
26
|
end
|
27
27
|
|
28
|
+
def total_price
|
29
|
+
line_items.map(&:price).sum
|
30
|
+
end
|
31
|
+
|
32
|
+
def total_eot_price
|
33
|
+
line_items.map(&:eot_price).sum
|
34
|
+
end
|
35
|
+
|
36
|
+
def total_vat
|
37
|
+
line_items.map(&:vat).sum
|
38
|
+
end
|
39
|
+
|
28
40
|
def total_quantity
|
29
41
|
line_items.map(&:quantity).sum
|
30
42
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Stall
|
2
|
+
class PaymentNotificationService < Stall::BaseService
|
3
|
+
attr_reader :request
|
4
|
+
|
5
|
+
def initialize(request)
|
6
|
+
@request = request
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
gateway.process_payment_for(request)
|
11
|
+
end
|
12
|
+
|
13
|
+
def rendering_options
|
14
|
+
gateway.rendering_options
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def gateway
|
20
|
+
@gateway ||= gateway_class.new(cart)
|
21
|
+
end
|
22
|
+
|
23
|
+
def gateway_class
|
24
|
+
@gateway_class ||= Stall::Payments.gateways[request.params[:gateway]]
|
25
|
+
end
|
26
|
+
|
27
|
+
def cart
|
28
|
+
@cart ||= Stall::Cart.find(gateway_class.cart_id_from(request))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
%h1= t('stall.checkout.payment.title')
|
2
|
+
|
3
|
+
%table.table.table-striped
|
4
|
+
%thead
|
5
|
+
%tr
|
6
|
+
%th= Stall::LineItem.human_attribute_name(:name)
|
7
|
+
%th= Stall::LineItem.human_attribute_name(:unit_price)
|
8
|
+
%th= Stall::LineItem.human_attribute_name(:quantity)
|
9
|
+
%th= Stall::LineItem.human_attribute_name(:price)
|
10
|
+
%tbody
|
11
|
+
- cart.line_items.each do |line_item|
|
12
|
+
%tr{ data: { :'line-item-id' => line_item.id } }
|
13
|
+
%td= line_item.name
|
14
|
+
%td= number_to_currency(line_item.unit_price)
|
15
|
+
%td= line_item.quantity
|
16
|
+
%td= number_to_currency(line_item.price)
|
17
|
+
|
18
|
+
%tr
|
19
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_eot_price')
|
20
|
+
%td= number_to_currency(cart.total_eot_price)
|
21
|
+
|
22
|
+
%tr
|
23
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_vat')
|
24
|
+
%td= number_to_currency(cart.total_vat)
|
25
|
+
|
26
|
+
%tr
|
27
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_price')
|
28
|
+
%td= number_to_currency(cart.total_price)
|
@@ -1 +1,11 @@
|
|
1
1
|
%h1= t('stall.checkout.payment_method.title')
|
2
|
+
|
3
|
+
= simple_form_for cart, as: :cart, url: step_path(cart) do |form|
|
4
|
+
= form.fields_for :payment do |payment_fields|
|
5
|
+
= payment_fields.association :payment_method, as: :radio_buttons
|
6
|
+
|
7
|
+
.form-actions
|
8
|
+
%button.btn.btn-primary{ type: 'submit' }
|
9
|
+
= t('stall.checkout.payment_method.validate')
|
10
|
+
|
11
|
+
|
@@ -15,6 +15,18 @@
|
|
15
15
|
%td= line_item_fields.input_field :quantity, class: 'form-control', data: { :'quantity-field' => true }
|
16
16
|
%td= line_item_fields.object.price
|
17
17
|
|
18
|
+
%tr
|
19
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_eot_price')
|
20
|
+
%td= number_to_currency(@cart.total_eot_price)
|
21
|
+
|
22
|
+
%tr
|
23
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_vat')
|
24
|
+
%td= number_to_currency(@cart.total_vat)
|
25
|
+
|
26
|
+
%tr
|
27
|
+
%td{ colspan: 3 }= t('stall.carts.recap.total_price')
|
28
|
+
%td= number_to_currency(@cart.total_price)
|
29
|
+
|
18
30
|
.form-actions
|
19
31
|
%button.btn.btn-default{ type: 'submit' }
|
20
32
|
= t('stall.carts.recap.update')
|
@@ -3,7 +3,7 @@
|
|
3
3
|
= form.hidden_field :sellable_id
|
4
4
|
|
5
5
|
.input-group
|
6
|
-
= form.input_field :quantity, spinner: false, value: 1, class: 'form-group'
|
6
|
+
= form.input_field :quantity, spinner: false, value: 1, class: 'form-group', data: { :'quantity-field' => true }
|
7
7
|
|
8
8
|
%span.input-group-btn
|
9
9
|
%button.btn.btn-primary{ type: 'submit', style: 'font-size: 12px' }
|
data/config/locales/stall.fr.yml
CHANGED
@@ -4,11 +4,16 @@ fr:
|
|
4
4
|
close: "Fermer"
|
5
5
|
|
6
6
|
carts:
|
7
|
+
formats:
|
8
|
+
name: "Commande n°%{ref}"
|
7
9
|
flashes:
|
8
10
|
update:
|
9
11
|
success: "Votre panier a bien été mis à jour."
|
10
12
|
error: "Votre panier n'a pu être mis à jour, merci de vérifier les champs."
|
11
13
|
recap:
|
14
|
+
total_eot_price: "Prix total HT"
|
15
|
+
total_vat: "Total TVA"
|
16
|
+
total_price: "Prix total"
|
12
17
|
update: "Mettre à jour le panier"
|
13
18
|
validate: "Passer la commande"
|
14
19
|
|
@@ -37,6 +42,10 @@ fr:
|
|
37
42
|
payment:
|
38
43
|
title: "Paiement"
|
39
44
|
|
45
|
+
payments:
|
46
|
+
gateway:
|
47
|
+
pay: "Payer ma commande"
|
48
|
+
|
40
49
|
activerecord:
|
41
50
|
attributes:
|
42
51
|
stall/line_item:
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateStallPayments < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :stall_payments do |t|
|
4
|
+
t.references :payment_method, index: true
|
5
|
+
t.references :cart, index: true
|
6
|
+
t.datetime :paid_at
|
7
|
+
t.json :data
|
8
|
+
|
9
|
+
t.timestamps null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
add_foreign_key :stall_payments, :stall_product_lists, column: :cart_id
|
13
|
+
add_foreign_key :stall_payments, :stall_payment_methods, column: :payment_method_id
|
14
|
+
end
|
15
|
+
end
|
@@ -3,10 +3,14 @@ module Stall
|
|
3
3
|
class StepGenerator < ::Rails::Generators::NamedBase
|
4
4
|
source_root File.expand_path('../templates', __FILE__)
|
5
5
|
|
6
|
-
def
|
6
|
+
def copy_step_template
|
7
7
|
template 'step.rb.erb', "lib/#{ file_path }.rb"
|
8
8
|
end
|
9
9
|
|
10
|
+
def create_view_template
|
11
|
+
template 'step.html.haml.erb', "app/views/checkout/steps/_#{ base_file_name }.html.haml"
|
12
|
+
end
|
13
|
+
|
10
14
|
private
|
11
15
|
|
12
16
|
# Override provided file name to include checkout_wizard at the end and
|
@@ -15,6 +19,10 @@ module Stall
|
|
15
19
|
def file_name
|
16
20
|
@_file_name ||= [@file_name, 'checkout_step'].join('_')
|
17
21
|
end
|
22
|
+
|
23
|
+
def base_file_name
|
24
|
+
@file_name
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
@@ -1,2 +1,19 @@
|
|
1
1
|
class <%= class_name %> < Stall::Checkout::Step
|
2
|
+
# Prepare the `cart` before showing your view
|
3
|
+
#
|
4
|
+
# def prepare
|
5
|
+
# end
|
6
|
+
|
7
|
+
# Process `params` here, defaults to updating the cart with `cart_params`
|
8
|
+
# (params[:cart])
|
9
|
+
#
|
10
|
+
# def process
|
11
|
+
# end
|
12
|
+
|
13
|
+
# Handle the case where the step should be skipped by returning true from
|
14
|
+
# this method
|
15
|
+
#
|
16
|
+
# def skip?
|
17
|
+
# false
|
18
|
+
# end
|
2
19
|
end
|
@@ -44,4 +44,14 @@ Stall.configure do |config|
|
|
44
44
|
# Defaults to nil, which means all countries are available
|
45
45
|
#
|
46
46
|
# config.shipping.free_shipping.available = nil
|
47
|
+
|
48
|
+
# Allows hooking into checkout steps initialization in the steps controller
|
49
|
+
# to inject dependencies to all or a specific step
|
50
|
+
#
|
51
|
+
# config.steps_initialization do |step|
|
52
|
+
# # Add the `current_user` in all steps
|
53
|
+
# step.inject(:current_user, current_user)
|
54
|
+
# # Only inject in the :some step :
|
55
|
+
# step.inject(:ip, request.remote_ip) if SomeCheckoutStep === step
|
56
|
+
# end
|
47
57
|
end
|
data/lib/stall.rb
CHANGED
@@ -29,11 +29,14 @@ module Stall
|
|
29
29
|
def process_addresses
|
30
30
|
unless params[:use_another_address_for_billing]
|
31
31
|
# Remove submitted billing address
|
32
|
-
billing_ownership = cart.address_ownership_for(:billing)
|
33
|
-
|
32
|
+
if (billing_ownership = cart.address_ownership_for(:billing))
|
33
|
+
cart.address_ownerships.destroy(billing_ownership)
|
34
|
+
end
|
35
|
+
|
34
36
|
# Set shipping address as the billing one
|
35
|
-
shipping_ownership = cart.address_ownership_for(:shipping)
|
36
|
-
|
37
|
+
if (shipping_ownership = cart.address_ownership_for(:shipping))
|
38
|
+
shipping_ownership.billing = true
|
39
|
+
end
|
37
40
|
end
|
38
41
|
end
|
39
42
|
end
|
data/lib/stall/checkout/step.rb
CHANGED
@@ -10,6 +10,12 @@ module Stall
|
|
10
10
|
@params = params
|
11
11
|
end
|
12
12
|
|
13
|
+
# Allow injecting dependencies on step initialization and accessing
|
14
|
+
# them as instance method in subclasses
|
15
|
+
def inject(method, content)
|
16
|
+
define_singleton_method(method, -> { content })
|
17
|
+
end
|
18
|
+
|
13
19
|
# Allows for preparing to the cart for the current step before rendering
|
14
20
|
# the step's view
|
15
21
|
#
|
@@ -23,7 +29,11 @@ module Stall
|
|
23
29
|
end
|
24
30
|
|
25
31
|
def cart_params
|
26
|
-
params.require(:cart).permit!
|
32
|
+
@cart_params ||= params.require(:cart).permit!
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip?
|
36
|
+
false
|
27
37
|
end
|
28
38
|
|
29
39
|
# Handles conversion from an identifier to a checkout step class, allowing
|
@@ -19,12 +19,24 @@ module Stall
|
|
19
19
|
@cart = cart
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
22
|
+
def initialize_current_step(params, &block)
|
23
|
+
step = current_step.new(cart, params)
|
24
|
+
# This block allows us to let the config inject controller-bound
|
25
|
+
# dependencies to the step just after it is initialized
|
26
|
+
block.call(step) if block
|
27
|
+
|
28
|
+
if step.skip?
|
29
|
+
validate_current_step!
|
30
|
+
initialize_current_step(params, &block)
|
31
|
+
else
|
32
|
+
step
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
36
|
+
def current_step
|
37
|
+
Stall::Checkout::Step.for(current_step_name)
|
38
|
+
end
|
39
|
+
|
28
40
|
def current_step_name
|
29
41
|
if step?(cart.state)
|
30
42
|
cart.state
|
data/lib/stall/config.rb
CHANGED
@@ -20,8 +20,19 @@ module Stall
|
|
20
20
|
# Default product weight if no weight is found
|
21
21
|
param :default_product_weight, 0
|
22
22
|
|
23
|
+
# Default step initialization hook
|
24
|
+
param :_steps_initialization_callback
|
25
|
+
|
23
26
|
def shipping
|
24
27
|
@shipping ||= Stall::Shipping::Config.new
|
25
28
|
end
|
29
|
+
|
30
|
+
def steps_initialization(value = nil, &block)
|
31
|
+
if (value ||= block)
|
32
|
+
@_steps_initialization_callback = value
|
33
|
+
else
|
34
|
+
@_steps_initialization_callback
|
35
|
+
end
|
36
|
+
end
|
26
37
|
end
|
27
38
|
end
|
data/lib/stall/engine.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
module Stall
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
-
initializer 'include sellable mixin into models' do
|
4
|
-
ActiveSupport.on_load(:active_record) do
|
5
|
-
include Stall::Sellable::Mixin
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
3
|
initializer 'set money gem default currency' do
|
10
4
|
Money.default_currency = Stall.config.default_currency
|
11
5
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Stall
|
2
|
+
module Payments
|
3
|
+
class Gateway
|
4
|
+
TRANSACTION_ID_FORMAT = 'ESHOP-%{cart_id}-%{transaction_index}'
|
5
|
+
|
6
|
+
attr_reader :cart
|
7
|
+
|
8
|
+
def initialize(cart)
|
9
|
+
@cart = cart
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register(name)
|
13
|
+
Stall::Payments.gateways[name] = self
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.cart_id_from(_request)
|
17
|
+
raise NoMethodError,
|
18
|
+
'Subclasses must implement the .cart_id_from(request) class method ' \
|
19
|
+
'to allow retrieving the cart from the remote gateway notification ' \
|
20
|
+
'request object'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.cart_id_from_transaction_id(transaction_id)
|
24
|
+
transaction_id && transaction_id.split('-')[2].to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def process_payment_for(_request)
|
28
|
+
raise NoMethodError,
|
29
|
+
'Subclasses must implement the #process_payment_for(request) ' \
|
30
|
+
'method to handle payment verifications and cart payment validation'
|
31
|
+
end
|
32
|
+
|
33
|
+
def transaction_id
|
34
|
+
@transaction_id ||= begin
|
35
|
+
unless (id = cart.payment.transaction_id)
|
36
|
+
id = next_transaction_id
|
37
|
+
cart.payment.update_attributes(transaction_id: id)
|
38
|
+
end
|
39
|
+
|
40
|
+
id
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Defines the arguments passed to the render call in response to the
|
45
|
+
# automatic gateway response notification
|
46
|
+
#
|
47
|
+
# Most of the gateways expect some specific return, so this is to be
|
48
|
+
# overriden by subclasses
|
49
|
+
def rendering_options
|
50
|
+
{ text: nil }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def next_transaction_id
|
56
|
+
if (last_transaction = Stall::Payment.order("data->>'transaction_id' DESC").select(:data).first)
|
57
|
+
if (id = last_transaction.transaction_id)
|
58
|
+
index = id.split('-').pop.to_i + 1
|
59
|
+
return transaction_id_for(index)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
transaction_id_for(1)
|
64
|
+
end
|
65
|
+
|
66
|
+
def transaction_id_for(index)
|
67
|
+
TRANSACTION_ID_FORMAT
|
68
|
+
.gsub('%{cart_id}', cart.reference)
|
69
|
+
.gsub('%{transaction_index}', ('%05d' % index))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/stall/routes.rb
CHANGED
@@ -6,20 +6,20 @@ module Stall
|
|
6
6
|
@router = router
|
7
7
|
end
|
8
8
|
|
9
|
-
def draw(mount_location
|
9
|
+
def draw(mount_location)
|
10
10
|
router.instance_eval do
|
11
|
-
scope mount_location do
|
12
|
-
|
13
|
-
resources :
|
14
|
-
|
15
|
-
end
|
11
|
+
scope mount_location, module: :stall do
|
12
|
+
resources :carts do
|
13
|
+
resources :line_items
|
14
|
+
end
|
16
15
|
|
17
|
-
|
16
|
+
resources :checkouts, only: [:show]
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
18
|
+
scope '/checkout/:type/:cart_id', module: 'checkout', as: :checkout do
|
19
|
+
resource :step, only: [:show, :update]
|
22
20
|
end
|
21
|
+
|
22
|
+
match '/payments/:gateway/process' => 'payments#process', via: [:get, :post]
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/lib/stall/sellable.rb
CHANGED
@@ -1,8 +1,43 @@
|
|
1
1
|
module Stall
|
2
2
|
module Sellable
|
3
|
-
extend ActiveSupport::
|
3
|
+
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
included do
|
6
|
+
has_many :line_items, class_name: 'Stall::LineItem',
|
7
|
+
dependent: :nullify,
|
8
|
+
as: :sellable,
|
9
|
+
inverse_of: :sellable
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_line_item
|
13
|
+
line_items.build(
|
14
|
+
name: (respond_to?(:name) && name) || (respond_to?(:title) && title),
|
15
|
+
unit_price: (respond_to?(:price) && price),
|
16
|
+
unit_eot_price: eot_price,
|
17
|
+
vat_rate: vat_rate,
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def vat_ratio
|
22
|
+
(vat_rate / 100.0) + 1
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_eot_price
|
28
|
+
price && (price / vat_ratio)
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_vat_rate
|
32
|
+
@default_vat_rate ||= Stall.config.vat_rate
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name, *args, &block)
|
36
|
+
if [:eot_price, :vat_rate].include?(name)
|
37
|
+
send(:"default_#{ name }")
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
7
42
|
end
|
8
43
|
end
|
data/lib/stall/utils.rb
CHANGED
data/lib/stall/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vala
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -248,6 +248,20 @@ dependencies:
|
|
248
248
|
- - "~>"
|
249
249
|
- !ruby/object:Gem::Version
|
250
250
|
version: 0.36.0
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: codeclimate-test-reporter
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
251
265
|
description: Rails e-commerce framework
|
252
266
|
email:
|
253
267
|
- vala@glyph.fr
|
@@ -269,23 +283,27 @@ files:
|
|
269
283
|
- app/controllers/stall/checkout/steps_controller.rb
|
270
284
|
- app/controllers/stall/checkouts_controller.rb
|
271
285
|
- app/controllers/stall/line_items_controller.rb
|
286
|
+
- app/controllers/stall/payments_controller.rb
|
272
287
|
- app/helpers/stall/add_to_cart_helper.rb
|
273
288
|
- app/helpers/stall/application_helper.rb
|
274
289
|
- app/helpers/stall/cart_helper.rb
|
275
290
|
- app/helpers/stall/checkout_helper.rb
|
276
|
-
- app/models/stall.rb
|
277
291
|
- app/models/stall/address.rb
|
278
292
|
- app/models/stall/address_ownership.rb
|
279
293
|
- app/models/stall/cart.rb
|
280
294
|
- app/models/stall/customer.rb
|
281
295
|
- app/models/stall/line_item.rb
|
296
|
+
- app/models/stall/payment.rb
|
297
|
+
- app/models/stall/payment_method.rb
|
282
298
|
- app/models/stall/product_list.rb
|
283
299
|
- app/models/stall/shipment.rb
|
284
300
|
- app/models/stall/shipping_method.rb
|
285
301
|
- app/services/stall/add_to_cart_service.rb
|
286
302
|
- app/services/stall/base_service.rb
|
303
|
+
- app/services/stall/payment_notification_service.rb
|
287
304
|
- app/services/stall/shipping_fee_calculator_service.rb
|
288
305
|
- app/views/checkout/steps/_informations.html.haml
|
306
|
+
- app/views/checkout/steps/_payment.html.haml
|
289
307
|
- app/views/checkout/steps/_payment_method.html.haml
|
290
308
|
- app/views/checkout/steps/_shipping_method.html.haml
|
291
309
|
- app/views/layouts/stall/application.html.erb
|
@@ -303,7 +321,11 @@ files:
|
|
303
321
|
- db/migrate/20160122143748_create_stall_address_ownerships.rb
|
304
322
|
- db/migrate/20160124014144_create_stall_shipping_methods.rb
|
305
323
|
- db/migrate/20160124020313_create_stall_shipments.rb
|
324
|
+
- db/migrate/20160125100733_create_stall_payment_methods.rb
|
325
|
+
- db/migrate/20160125100734_create_stall_payments.rb
|
326
|
+
- db/migrate/20160127113619_add_user_to_customer.rb
|
306
327
|
- lib/generators/stall/checkout/step/step_generator.rb
|
328
|
+
- lib/generators/stall/checkout/step/templates/step.html.haml.erb
|
307
329
|
- lib/generators/stall/checkout/step/templates/step.rb.erb
|
308
330
|
- lib/generators/stall/checkout/wizard/templates/wizard.rb.erb
|
309
331
|
- lib/generators/stall/checkout/wizard/wizard_generator.rb
|
@@ -320,12 +342,12 @@ files:
|
|
320
342
|
- lib/stall/checkout/wizard.rb
|
321
343
|
- lib/stall/config.rb
|
322
344
|
- lib/stall/engine.rb
|
345
|
+
- lib/stall/payments.rb
|
346
|
+
- lib/stall/payments/gateway.rb
|
323
347
|
- lib/stall/rails/currency_helper.rb
|
324
348
|
- lib/stall/rails/routing_mapper.rb
|
325
349
|
- lib/stall/routes.rb
|
326
350
|
- lib/stall/sellable.rb
|
327
|
-
- lib/stall/sellable/mixin.rb
|
328
|
-
- lib/stall/sellable/model.rb
|
329
351
|
- lib/stall/shipping.rb
|
330
352
|
- lib/stall/shipping/calculator.rb
|
331
353
|
- lib/stall/shipping/config.rb
|
data/app/models/stall.rb
DELETED
data/lib/stall/sellable/mixin.rb
DELETED
data/lib/stall/sellable/model.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module Stall
|
2
|
-
module Sellable
|
3
|
-
module Model
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
def sellable?
|
7
|
-
true
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_line_item
|
11
|
-
value_if_method = -> name { send(name) if respond_to?(name) }
|
12
|
-
|
13
|
-
Stall::LineItem.new(
|
14
|
-
sellable: self,
|
15
|
-
quantity: 1,
|
16
|
-
name: (value_if_method.(:name) || value_if_method.(:title)),
|
17
|
-
unit_price: value_if_method.(:price),
|
18
|
-
unit_eot_price: eot_price,
|
19
|
-
vat_rate: vat_rate,
|
20
|
-
)
|
21
|
-
end
|
22
|
-
|
23
|
-
def vat_ratio
|
24
|
-
(vat_rate / 100.0) + 1
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def default_eot_price
|
30
|
-
price && (price / vat_ratio)
|
31
|
-
end
|
32
|
-
|
33
|
-
def default_vat_rate
|
34
|
-
@default_vat_rate ||= Stall.config.vat_rate
|
35
|
-
end
|
36
|
-
|
37
|
-
def method_missing(name, *args, &block)
|
38
|
-
if [:eot_price, :vat_rate].include?(name)
|
39
|
-
send(:"default_#{ name }")
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|