effective_orders 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +856 -0
- data/Rakefile +24 -0
- data/app/assets/images/effective_orders/stripe_connect.png +0 -0
- data/app/assets/javascripts/effective_orders/shipping_address_toggle.js.coffee +30 -0
- data/app/assets/javascripts/effective_orders/stripe_charges.js.coffee +26 -0
- data/app/assets/javascripts/effective_orders/stripe_subscriptions.js.coffee +28 -0
- data/app/assets/javascripts/effective_orders.js +2 -0
- data/app/assets/stylesheets/effective_orders/_order.scss +30 -0
- data/app/assets/stylesheets/effective_orders.css.scss +1 -0
- data/app/controllers/admin/customers_controller.rb +15 -0
- data/app/controllers/admin/orders_controller.rb +22 -0
- data/app/controllers/effective/carts_controller.rb +70 -0
- data/app/controllers/effective/orders_controller.rb +191 -0
- data/app/controllers/effective/providers/moneris.rb +94 -0
- data/app/controllers/effective/providers/paypal.rb +29 -0
- data/app/controllers/effective/providers/stripe.rb +125 -0
- data/app/controllers/effective/providers/stripe_connect.rb +47 -0
- data/app/controllers/effective/subscriptions_controller.rb +123 -0
- data/app/controllers/effective/webhooks_controller.rb +86 -0
- data/app/helpers/effective_carts_helper.rb +90 -0
- data/app/helpers/effective_orders_helper.rb +108 -0
- data/app/helpers/effective_paypal_helper.rb +37 -0
- data/app/helpers/effective_stripe_helper.rb +63 -0
- data/app/mailers/effective/orders_mailer.rb +64 -0
- data/app/models/concerns/acts_as_purchasable.rb +134 -0
- data/app/models/effective/access_denied.rb +17 -0
- data/app/models/effective/cart.rb +65 -0
- data/app/models/effective/cart_item.rb +40 -0
- data/app/models/effective/customer.rb +61 -0
- data/app/models/effective/datatables/customers.rb +45 -0
- data/app/models/effective/datatables/orders.rb +53 -0
- data/app/models/effective/order.rb +247 -0
- data/app/models/effective/order_item.rb +69 -0
- data/app/models/effective/stripe_charge.rb +35 -0
- data/app/models/effective/subscription.rb +95 -0
- data/app/models/inputs/price_field.rb +63 -0
- data/app/models/inputs/price_form_input.rb +7 -0
- data/app/models/inputs/price_formtastic_input.rb +9 -0
- data/app/models/inputs/price_input.rb +19 -0
- data/app/models/inputs/price_simple_form_input.rb +8 -0
- data/app/models/validators/effective/sold_out_validator.rb +7 -0
- data/app/views/active_admin/effective_orders/orders/_show.html.haml +70 -0
- data/app/views/admin/customers/_actions.html.haml +2 -0
- data/app/views/admin/customers/index.html.haml +10 -0
- data/app/views/admin/orders/index.html.haml +7 -0
- data/app/views/admin/orders/show.html.haml +11 -0
- data/app/views/effective/carts/_cart.html.haml +33 -0
- data/app/views/effective/carts/show.html.haml +18 -0
- data/app/views/effective/orders/_checkout_step_1.html.haml +39 -0
- data/app/views/effective/orders/_checkout_step_2.html.haml +18 -0
- data/app/views/effective/orders/_my_purchases.html.haml +15 -0
- data/app/views/effective/orders/_order.html.haml +4 -0
- data/app/views/effective/orders/_order_header.html.haml +21 -0
- data/app/views/effective/orders/_order_items.html.haml +39 -0
- data/app/views/effective/orders/_order_payment_details.html.haml +11 -0
- data/app/views/effective/orders/_order_shipping.html.haml +19 -0
- data/app/views/effective/orders/_order_user_fields.html.haml +10 -0
- data/app/views/effective/orders/checkout.html.haml +3 -0
- data/app/views/effective/orders/declined.html.haml +10 -0
- data/app/views/effective/orders/moneris/_form.html.haml +34 -0
- data/app/views/effective/orders/my_purchases.html.haml +6 -0
- data/app/views/effective/orders/my_sales.html.haml +28 -0
- data/app/views/effective/orders/new.html.haml +4 -0
- data/app/views/effective/orders/paypal/_form.html.haml +5 -0
- data/app/views/effective/orders/purchased.html.haml +10 -0
- data/app/views/effective/orders/show.html.haml +17 -0
- data/app/views/effective/orders/stripe/_form.html.haml +8 -0
- data/app/views/effective/orders/stripe/_subscription_fields.html.haml +7 -0
- data/app/views/effective/orders_mailer/order_receipt_to_admin.html.haml +8 -0
- data/app/views/effective/orders_mailer/order_receipt_to_buyer.html.haml +8 -0
- data/app/views/effective/orders_mailer/order_receipt_to_seller.html.haml +30 -0
- data/app/views/effective/subscriptions/index.html.haml +16 -0
- data/app/views/effective/subscriptions/new.html.haml +10 -0
- data/app/views/effective/subscriptions/show.html.haml +49 -0
- data/config/routes.rb +57 -0
- data/db/migrate/01_create_effective_orders.rb.erb +91 -0
- data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +29 -0
- data/db/upgrade/upgrade_price_column_on_table.rb.erb +17 -0
- data/lib/effective_orders/engine.rb +52 -0
- data/lib/effective_orders/version.rb +3 -0
- data/lib/effective_orders.rb +76 -0
- data/lib/generators/effective_orders/install_generator.rb +38 -0
- data/lib/generators/effective_orders/upgrade_from03x_generator.rb +34 -0
- data/lib/generators/effective_orders/upgrade_price_column_generator.rb +34 -0
- data/lib/generators/templates/README +1 -0
- data/lib/generators/templates/effective_orders.rb +210 -0
- data/spec/controllers/carts_controller_spec.rb +143 -0
- data/spec/controllers/moneris_orders_controller_spec.rb +245 -0
- data/spec/controllers/orders_controller_spec.rb +418 -0
- data/spec/controllers/stripe_orders_controller_spec.rb +127 -0
- data/spec/controllers/webhooks_controller_spec.rb +79 -0
- data/spec/dummy/README.rdoc +8 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/product.rb +17 -0
- data/spec/dummy/app/models/product_with_float_price.rb +17 -0
- data/spec/dummy/app/models/user.rb +28 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +31 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +83 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +254 -0
- data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
- data/spec/dummy/config/initializers/effective_orders.rb +22 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +142 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +487 -0
- data/spec/dummy/log/test.log +347 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/helpers/effective_orders_helper_spec.rb +21 -0
- data/spec/models/acts_as_purchasable_spec.rb +107 -0
- data/spec/models/customer_spec.rb +71 -0
- data/spec/models/factories_spec.rb +13 -0
- data/spec/models/order_item_spec.rb +35 -0
- data/spec/models/order_spec.rb +323 -0
- data/spec/models/stripe_charge_spec.rb +39 -0
- data/spec/models/subscription_spec.rb +103 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/factories.rb +118 -0
- metadata +387 -0
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
# Our tasks
|
9
|
+
load 'lib/tasks/effective_orders_tasks.rake'
|
10
|
+
|
11
|
+
# Testing tasks
|
12
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
13
|
+
load 'rails/tasks/engine.rake'
|
14
|
+
|
15
|
+
require "bundler/vendored_thor"
|
16
|
+
Bundler::GemHelper.install_tasks
|
17
|
+
|
18
|
+
require 'rspec/core'
|
19
|
+
require 'rspec/core/rake_task'
|
20
|
+
|
21
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
22
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
23
|
+
|
24
|
+
task :default => :spec
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
hideShippingAddressFields = (shipping_address) ->
|
2
|
+
shipping_address.hide().find('input,select').prop('required', false)
|
3
|
+
|
4
|
+
showShippingAddressFields = (shipping_address) ->
|
5
|
+
shipping_address.show().find("input:not([name$='[address2]']),select:not([name$='[state_code]'])").prop('required', true)
|
6
|
+
|
7
|
+
initShippingAddressFields = ->
|
8
|
+
effective_order = $('.effective-order').first()
|
9
|
+
|
10
|
+
if effective_order.length > 0
|
11
|
+
shipping_address_same_as_billing = effective_order.find('#effective_order_shipping_address_same_as_billing')
|
12
|
+
shipping_address = effective_order.find('.shipping_address_fields')
|
13
|
+
|
14
|
+
if shipping_address_same_as_billing.length > 0 && shipping_address.length > 0
|
15
|
+
if shipping_address_same_as_billing.is(':checked')
|
16
|
+
hideShippingAddressFields(shipping_address)
|
17
|
+
else
|
18
|
+
showShippingAddressFields(shipping_address)
|
19
|
+
|
20
|
+
$ -> initShippingAddressFields()
|
21
|
+
$(document).on 'page:change', -> initShippingAddressFields()
|
22
|
+
|
23
|
+
$(document).on 'change', '#effective_order_shipping_address_same_as_billing', (event) ->
|
24
|
+
obj = $(event.currentTarget)
|
25
|
+
shipping_address = obj.closest('form').find('.shipping_address_fields')
|
26
|
+
|
27
|
+
if obj.is(':checked')
|
28
|
+
hideShippingAddressFields(shipping_address)
|
29
|
+
else
|
30
|
+
showShippingAddressFields(shipping_address)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
stripeCheckoutHandler = (key, form) ->
|
2
|
+
StripeCheckout.configure
|
3
|
+
key: key
|
4
|
+
token: (token, args) ->
|
5
|
+
if token.error
|
6
|
+
form.find("input[type='submit']").prop('disabled', false)
|
7
|
+
alert("An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}")
|
8
|
+
else
|
9
|
+
form.find("input[type='submit']").prop('disabled', true)
|
10
|
+
form.find('input#effective_stripe_charge_token').val('' + token['id'])
|
11
|
+
form.submit()
|
12
|
+
|
13
|
+
$(document).on 'click', "#effective-orders-new-charge-form form input[type='submit']", (event) ->
|
14
|
+
event.preventDefault()
|
15
|
+
|
16
|
+
obj = $('#effective-orders-new-charge-form')
|
17
|
+
form = obj.find('form').first()
|
18
|
+
|
19
|
+
form.find("input[type='submit']").prop('disabled', true)
|
20
|
+
|
21
|
+
stripeCheckoutHandler(obj.data('stripe-publishable-key'), form).open
|
22
|
+
name: obj.data('site-title')
|
23
|
+
email: obj.data('user-email')
|
24
|
+
description: obj.data('description')
|
25
|
+
amount: obj.data('amount')
|
26
|
+
closed: -> form.find("input[type='submit']").prop('disabled', false)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
stripeCheckoutHandler = (key, form) ->
|
2
|
+
StripeCheckout.configure
|
3
|
+
key: key
|
4
|
+
token: (token, args) ->
|
5
|
+
if token.error
|
6
|
+
form.find("input[type='submit']").prop('disabled', false)
|
7
|
+
alert("An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}")
|
8
|
+
else
|
9
|
+
form.find("input[type='submit']").prop('disabled', true)
|
10
|
+
form.find('input#effective_stripe_subscription_token').val('' + token['id'])
|
11
|
+
form.submit()
|
12
|
+
|
13
|
+
$(document).on 'click', "#effective-orders-new-subscription-form form input[type='submit']", (event) ->
|
14
|
+
event.preventDefault()
|
15
|
+
|
16
|
+
obj = $('#effective-orders-new-subscription-form')
|
17
|
+
form = obj.find('form').first()
|
18
|
+
plan = form.find('option:selected')
|
19
|
+
|
20
|
+
if plan.length > 0
|
21
|
+
form.find("input[type='submit']").prop('disabled', true)
|
22
|
+
|
23
|
+
stripeCheckoutHandler(obj.data('stripe-publishable-key'), form).open
|
24
|
+
name: obj.data('site-title')
|
25
|
+
email: obj.data('user-email')
|
26
|
+
description: plan.text()
|
27
|
+
panelLabel: 'Start Subscription'
|
28
|
+
closed: -> form.find("input[type='submit']").prop('disabled', false)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
.effective-order {
|
2
|
+
.table {
|
3
|
+
clear: both;
|
4
|
+
|
5
|
+
tfoot {
|
6
|
+
> tr:first-child {
|
7
|
+
border-top: 20px solid transparent;
|
8
|
+
}
|
9
|
+
|
10
|
+
th {
|
11
|
+
border: none;
|
12
|
+
text-align: right;
|
13
|
+
padding-right: 65px;
|
14
|
+
}
|
15
|
+
|
16
|
+
.actions { border: none; }
|
17
|
+
}
|
18
|
+
|
19
|
+
.price { text-align: right; }
|
20
|
+
}
|
21
|
+
|
22
|
+
.effective-stripe-subscription {
|
23
|
+
input {
|
24
|
+
margin-top: 5px;
|
25
|
+
width: 30%;
|
26
|
+
min-width: 200px;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
@import "effective_orders/order";
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Admin
|
2
|
+
class CustomersController < ApplicationController
|
3
|
+
before_filter :authenticate_user! # This is devise, ensure we're logged in.
|
4
|
+
|
5
|
+
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:admin_customers] : EffectiveOrders.layout)
|
6
|
+
|
7
|
+
def index
|
8
|
+
@datatable = Effective::Datatables::Customers.new() if defined?(EffectiveDatatables)
|
9
|
+
@page_title = 'Customers'
|
10
|
+
|
11
|
+
EffectiveOrders.authorized?(self, :index, Effective::Customer)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Admin
|
2
|
+
class OrdersController < ApplicationController
|
3
|
+
before_filter :authenticate_user! # This is devise, ensure we're logged in.
|
4
|
+
|
5
|
+
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:admin_orders] : EffectiveOrders.layout)
|
6
|
+
|
7
|
+
def index
|
8
|
+
@datatable = Effective::Datatables::Orders.new() if defined?(EffectiveDatatables)
|
9
|
+
@page_title = 'Orders'
|
10
|
+
|
11
|
+
EffectiveOrders.authorized?(self, :index, Effective::Order)
|
12
|
+
end
|
13
|
+
|
14
|
+
def show
|
15
|
+
@order = Effective::Order.find(params[:id])
|
16
|
+
@page_title = "Order ##{@order.to_param}"
|
17
|
+
|
18
|
+
EffectiveOrders.authorized?(self, :show, @order)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Effective
|
2
|
+
class CartsController < ApplicationController
|
3
|
+
include EffectiveCartsHelper
|
4
|
+
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:carts] : EffectiveOrders.layout)
|
5
|
+
|
6
|
+
def show
|
7
|
+
@cart = current_cart
|
8
|
+
@page_title ||= 'Shopping Cart'
|
9
|
+
EffectiveOrders.authorized?(self, :show, @cart)
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy
|
13
|
+
@cart = current_cart
|
14
|
+
|
15
|
+
EffectiveOrders.authorized?(self, :destroy, @cart)
|
16
|
+
|
17
|
+
if @cart.destroy
|
18
|
+
flash[:success] = 'Successfully emptied cart'
|
19
|
+
else
|
20
|
+
flash[:danger] = 'Unable to destroy cart:' + e.message
|
21
|
+
end
|
22
|
+
|
23
|
+
request.referrer ? (redirect_to :back) : (redirect_to effective_orders.cart_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_to_cart
|
27
|
+
@purchasable = (add_to_cart_params[:purchasable_type].constantize.find(add_to_cart_params[:purchasable_id].to_i) rescue nil)
|
28
|
+
|
29
|
+
EffectiveOrders.authorized?(self, :update, current_cart)
|
30
|
+
|
31
|
+
begin
|
32
|
+
raise "please select a valid #{add_to_cart_params[:purchasable_type] || 'item' }" unless @purchasable
|
33
|
+
|
34
|
+
current_cart.add_to_cart(@purchasable, [add_to_cart_params[:quantity].to_i, 1].max)
|
35
|
+
flash[:success] = 'Successfully added item to cart'
|
36
|
+
rescue EffectiveOrders::SoldOutException
|
37
|
+
flash[:warning] = 'This item is sold out'
|
38
|
+
rescue => e
|
39
|
+
flash[:danger] = 'Unable to add item to cart: ' + e.message
|
40
|
+
end
|
41
|
+
|
42
|
+
request.referrer ? (redirect_to :back) : (redirect_to effective_orders.cart_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_from_cart
|
46
|
+
@cart_item = current_cart.cart_items.find(remove_from_cart_params[:id])
|
47
|
+
|
48
|
+
EffectiveOrders.authorized?(self, :update, current_cart)
|
49
|
+
|
50
|
+
if @cart_item.destroy
|
51
|
+
flash[:success] = 'Successfully removed item from cart'
|
52
|
+
else
|
53
|
+
flash[:danger] = 'Unable to remove item from cart: ' + e.message
|
54
|
+
end
|
55
|
+
|
56
|
+
request.referrer ? (redirect_to :back) : (redirect_to effective_orders.cart_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def add_to_cart_params
|
62
|
+
params.permit(:purchasable_type, :purchasable_id, :quantity)
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove_from_cart_params
|
66
|
+
params.permit(:id)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module Effective
|
2
|
+
class OrdersController < ApplicationController
|
3
|
+
include EffectiveCartsHelper
|
4
|
+
|
5
|
+
include Providers::Moneris if EffectiveOrders.moneris_enabled
|
6
|
+
include Providers::Paypal if EffectiveOrders.paypal_enabled
|
7
|
+
include Providers::Stripe if EffectiveOrders.stripe_enabled
|
8
|
+
include Providers::StripeConnect if EffectiveOrders.stripe_connect_enabled
|
9
|
+
|
10
|
+
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:orders] : EffectiveOrders.layout)
|
11
|
+
|
12
|
+
before_filter :authenticate_user!, :except => [:paypal_postback]
|
13
|
+
before_filter :set_page_title
|
14
|
+
|
15
|
+
# This is the entry point for the "Checkout" buttons
|
16
|
+
def new
|
17
|
+
@order ||= Order.new(current_cart, current_user)
|
18
|
+
|
19
|
+
EffectiveOrders.authorized?(self, :new, @order)
|
20
|
+
|
21
|
+
# We're only going to check for a subset of errors on this step,
|
22
|
+
# with the idea that we don't want to create an Order object if the Order is totally invalid
|
23
|
+
@order.valid?
|
24
|
+
|
25
|
+
if @order.errors[:order_items].present?
|
26
|
+
flash[:danger] = @order.errors[:order_items].first
|
27
|
+
redirect_to effective_orders.cart_path
|
28
|
+
elsif @order.errors[:total].present?
|
29
|
+
flash[:danger] = @order.errors[:total].first.gsub(EffectiveOrders.minimum_charge.to_i.to_s, view_context.price_to_currency(EffectiveOrders.minimum_charge.to_i))
|
30
|
+
redirect_to effective_orders.cart_path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
@order = Order.new(current_cart, current_user)
|
36
|
+
@order.attributes = order_params
|
37
|
+
@order.shipping_address = @order.billing_address if @order.shipping_address_same_as_billing?
|
38
|
+
|
39
|
+
EffectiveOrders.authorized?(self, :create, @order)
|
40
|
+
|
41
|
+
Effective::Order.transaction do
|
42
|
+
begin
|
43
|
+
if @order.save_billing_address? && @order.user.respond_to?(:billing_address)
|
44
|
+
@order.user.billing_address = @order.billing_address
|
45
|
+
end
|
46
|
+
|
47
|
+
if @order.save_shipping_address? && @order.user.respond_to?(:shipping_address)
|
48
|
+
@order.user.shipping_address = @order.shipping_address
|
49
|
+
end
|
50
|
+
|
51
|
+
@order.save!
|
52
|
+
|
53
|
+
if @order.total < 0.01 && @order.total >= 0.00 && EffectiveOrders.allow_free_orders
|
54
|
+
order_purchased('zero-dollar order')
|
55
|
+
else
|
56
|
+
redirect_to(effective_orders.order_path(@order))
|
57
|
+
end
|
58
|
+
|
59
|
+
return
|
60
|
+
rescue => e
|
61
|
+
Rails.logger.info e.message
|
62
|
+
flash[:danger] = "An error has ocurred. Please try again. Message: #{e.message}"
|
63
|
+
raise ActiveRecord::Rollback
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
render :action => :new
|
68
|
+
end
|
69
|
+
|
70
|
+
def show
|
71
|
+
@order = Order.find(params[:id])
|
72
|
+
EffectiveOrders.authorized?(self, :show, @order)
|
73
|
+
|
74
|
+
if @order.purchased? == false
|
75
|
+
@page_title = 'Checkout'
|
76
|
+
render(:checkout) and return
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def index
|
82
|
+
redirect_to effective_orders.my_purchases_path
|
83
|
+
end
|
84
|
+
|
85
|
+
# Basically an index page.
|
86
|
+
# Purchases is an Order History page. List of purchased orders
|
87
|
+
def my_purchases
|
88
|
+
@orders = Order.purchased_by(current_user)
|
89
|
+
|
90
|
+
EffectiveOrders.authorized?(self, :index, Effective::Order)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Sales is a list of what products beign sold by me have been purchased
|
94
|
+
def my_sales
|
95
|
+
@order_items = OrderItem.sold_by(current_user)
|
96
|
+
|
97
|
+
EffectiveOrders.authorized?(self, :index, Effective::Order)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Thank you for Purchasing this Order. This is where a successfully purchased order ends up
|
101
|
+
def purchased # Thank You!
|
102
|
+
@order = Order.find(params[:id])
|
103
|
+
EffectiveOrders.authorized?(self, :show, @order)
|
104
|
+
end
|
105
|
+
|
106
|
+
# An error has occurred, please try again
|
107
|
+
def declined # An error occurred!
|
108
|
+
@order = Order.find(params[:id])
|
109
|
+
EffectiveOrders.authorized?(self, :show, @order)
|
110
|
+
end
|
111
|
+
|
112
|
+
def resend_buyer_receipt
|
113
|
+
@order = Effective::Order.find(params[:id])
|
114
|
+
EffectiveOrders.authorized?(self, :show, @order)
|
115
|
+
|
116
|
+
if (Effective::OrdersMailer.order_receipt_to_buyer(@order).deliver rescue false)
|
117
|
+
flash[:success] = "Successfully resent order receipt to #{@order.user.email}"
|
118
|
+
else
|
119
|
+
flash[:danger] = "Unable to send order receipt"
|
120
|
+
end
|
121
|
+
|
122
|
+
begin
|
123
|
+
redirect_to :back
|
124
|
+
rescue => e
|
125
|
+
redirect_to effective_orders.admin_orders_path
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def pretend_purchase
|
130
|
+
if Rails.env.development? || EffectiveOrders.allow_pretend_purchase_in_production
|
131
|
+
@order = Order.find(params[:id])
|
132
|
+
EffectiveOrders.authorized?(self, :update, @order)
|
133
|
+
order_purchased('for pretend', params[:purchased_redirect_url], params[:declined_redirect_url])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
def order_purchased(details = nil, redirect_url = nil, declined_redirect_url = nil)
|
140
|
+
begin
|
141
|
+
@order.purchase!(details)
|
142
|
+
current_cart.try(:destroy)
|
143
|
+
|
144
|
+
flash[:success] = "Successfully purchased order"
|
145
|
+
|
146
|
+
redirect_to (redirect_url.presence || effective_orders.order_purchased_path(@order)).gsub(':id', @order.id.to_s)
|
147
|
+
rescue => e
|
148
|
+
binding.pry
|
149
|
+
flash[:danger] = "Unable to process your order. Your card has not been charged. Your Cart items have been restored. Please try again. Error Message: #{e.message}"
|
150
|
+
redirect_to (declined_redirect_url.presence || effective_orders.cart_path).gsub(':id', @order.id.to_s)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def order_declined(details = nil, redirect_url = nil)
|
155
|
+
@order.decline!(details) rescue nil
|
156
|
+
|
157
|
+
flash[:danger] = "Unable to process your order. Your Cart items have been restored."
|
158
|
+
|
159
|
+
redirect_to (redirect_url.presence || effective_orders.order_declined_path(@order)).gsub(':id', @order.id.to_s)
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
# StrongParameters
|
165
|
+
def order_params
|
166
|
+
begin
|
167
|
+
params.require(:effective_order).permit(
|
168
|
+
:save_billing_address, :save_shipping_address, :shipping_address_same_as_billing,
|
169
|
+
:billing_address => [:full_name, :address1, :address2, :city, :country_code, :state_code, :postal_code],
|
170
|
+
:shipping_address => [:full_name, :address1, :address2, :city, :country_code, :state_code, :postal_code],
|
171
|
+
:user_attributes => (EffectiveOrders.collect_user_fields || []),
|
172
|
+
:order_items_attributes => [:stripe_coupon_id, :class, :id]
|
173
|
+
)
|
174
|
+
rescue => e
|
175
|
+
params[:effective_order] || {}
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def set_page_title
|
180
|
+
@page_title ||= case params[:action]
|
181
|
+
when 'my_purchases' ; 'Order History'
|
182
|
+
when 'my_sales' ; 'Sales History'
|
183
|
+
when 'purchased' ; 'Thank You'
|
184
|
+
when 'declined' ; 'Unable to process payment'
|
185
|
+
when 'show' ; 'Order Receipt'
|
186
|
+
else 'Checkout'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Effective
|
2
|
+
module Providers
|
3
|
+
module Moneris
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
prepend_before_filter :find_authenticity_token_from_moneris, :only => [:moneris_postback]
|
8
|
+
end
|
9
|
+
|
10
|
+
def moneris_postback
|
11
|
+
@order ||= Effective::Order.find(params[:response_order_id].to_i - EffectiveOrders.moneris[:order_nudge].to_i)
|
12
|
+
|
13
|
+
EffectiveOrders.authorized?(self, :update, @order)
|
14
|
+
|
15
|
+
# Store the Order Nudge if present, so we can have this information in our order_purchased hash
|
16
|
+
params[:order_nudge] = EffectiveOrders.moneris[:order_nudge] if EffectiveOrders.moneris[:order_nudge].to_i > 0
|
17
|
+
|
18
|
+
# Delete the Purchased and Declined Redirect URLs
|
19
|
+
purchased_redirect_url = params.delete(:rvar_purchased_redirect_url)
|
20
|
+
declined_redirect_url = params.delete(:rvar_declined_redirect_url)
|
21
|
+
|
22
|
+
if params[:result].to_s == '1' && params[:transactionKey].present?
|
23
|
+
verify_params = parse_moneris_response(send_moneris_verify_request(params[:transactionKey])) || {}
|
24
|
+
|
25
|
+
response_code = verify_params[:response_code].to_i # Sometimes moneris sends us the string 'null'
|
26
|
+
|
27
|
+
if response_code > 0 && response_code < 50 # Less than 50 means a successful validation
|
28
|
+
order_purchased(params.merge(verify_params), purchased_redirect_url)
|
29
|
+
else
|
30
|
+
order_declined(params.merge(verify_params), declined_redirect_url)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
order_declined(params, declined_redirect_url)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse_moneris_response(text)
|
40
|
+
text.split("<br>").inject(Hash.new()) { |h, i| h[i.split(' ').first.to_sym] = i.split(' ').last ; h } rescue {:response => text}
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_moneris_verify_request(verify_key)
|
44
|
+
`curl -F ps_store_id='#{EffectiveOrders.moneris[:ps_store_id]}' -F hpp_key='#{EffectiveOrders.moneris[:hpp_key]}' -F transactionKey='#{verify_key}' --referer #{effective_orders.moneris_postback_url} #{EffectiveOrders.moneris[:verify_url]}`
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_authenticity_token_from_moneris
|
48
|
+
params[:authenticity_token] = params.delete(:rvar_authenticity_token)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Instructions to set up a Test Moneris Store
|
57
|
+
|
58
|
+
# https://esqa.moneris.com/mpg/index.php
|
59
|
+
|
60
|
+
# demouser
|
61
|
+
# store2
|
62
|
+
# password
|
63
|
+
|
64
|
+
# Click on the ADMIN -> hosted config
|
65
|
+
|
66
|
+
# Generate a Version3 Configuration
|
67
|
+
|
68
|
+
# This should bring us to a "hosted Paypage Configuration"
|
69
|
+
|
70
|
+
# == Basic Configuration ==
|
71
|
+
# - Transaction Type: Purchase
|
72
|
+
# - Response Method Sent to your server as a POST
|
73
|
+
# - Approved URL: http://ourwebsite.com/orders/moneris_postback
|
74
|
+
# - Declined URL: http://ourwebsite.com/orders/moneris_postback
|
75
|
+
|
76
|
+
# == Appearance ==
|
77
|
+
# - Display item details
|
78
|
+
# - Display customer details
|
79
|
+
# - Display billing address details
|
80
|
+
# - Display merchant name
|
81
|
+
# - Cancel Button Text: Cancel Transaction
|
82
|
+
# - Cancel Button URL http://ourwebsite.com
|
83
|
+
|
84
|
+
# == Response Fields ==
|
85
|
+
# - Ignore, leave blank, the asynchronous data post
|
86
|
+
# - Do not Perform an asynchronous data post. Leave Async Response URL blank
|
87
|
+
|
88
|
+
# == Security ==
|
89
|
+
# Add a URL http://ourwebsite.com/orders/new
|
90
|
+
# Click YES Enable Transaction Verification
|
91
|
+
# Sent to your server as a POST
|
92
|
+
# Response URL: http://ourwebsite.com/orders/moneris_postback
|
93
|
+
|
94
|
+
# Displayed as key/value pairs on our server. ????
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Effective
|
2
|
+
module Providers
|
3
|
+
module Paypal
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
skip_before_filter :verify_authenticity_token, :only => [:paypal_postback]
|
8
|
+
end
|
9
|
+
|
10
|
+
def paypal_postback
|
11
|
+
@order ||= Effective::Order.where(:id => params[:invoice].to_i).first
|
12
|
+
|
13
|
+
EffectiveOrders.authorized?(self, :update, @order)
|
14
|
+
|
15
|
+
if @order.present?
|
16
|
+
if params[:payment_status] == 'Completed' && params[:custom] == EffectiveOrders.paypal[:secret]
|
17
|
+
order_purchased(params)
|
18
|
+
else
|
19
|
+
order_declined(params)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
head(:ok)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|