stall 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://api.travis-ci.org/rails-stall/stall.svg?branch=master)](http://travis-ci.org/rails-stall/stall)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/rails-stall/stall.svg)](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
|