merchant_sidekick 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gitignore +12 -0
  2. data/Changelog.md +38 -0
  3. data/Gemfile +2 -0
  4. data/MIT-LICENSE +19 -0
  5. data/README.md +88 -0
  6. data/Rakefile +10 -0
  7. data/lib/merchant_sidekick.rb +45 -0
  8. data/lib/merchant_sidekick/active_merchant/credit_card_payment.rb +117 -0
  9. data/lib/merchant_sidekick/active_merchant/gateways/authorize_net_gateway.rb +26 -0
  10. data/lib/merchant_sidekick/active_merchant/gateways/base.rb +29 -0
  11. data/lib/merchant_sidekick/active_merchant/gateways/bogus_gateway.rb +19 -0
  12. data/lib/merchant_sidekick/active_merchant/gateways/paypal_gateway.rb +43 -0
  13. data/lib/merchant_sidekick/addressable/address.rb +400 -0
  14. data/lib/merchant_sidekick/addressable/addressable.rb +353 -0
  15. data/lib/merchant_sidekick/buyer.rb +99 -0
  16. data/lib/merchant_sidekick/gateway.rb +81 -0
  17. data/lib/merchant_sidekick/install.rb +19 -0
  18. data/lib/merchant_sidekick/invoice.rb +179 -0
  19. data/lib/merchant_sidekick/line_item.rb +128 -0
  20. data/lib/merchant_sidekick/migrations/addressable.rb +47 -0
  21. data/lib/merchant_sidekick/migrations/billing.rb +100 -0
  22. data/lib/merchant_sidekick/migrations/shopping_cart.rb +28 -0
  23. data/lib/merchant_sidekick/money.rb +38 -0
  24. data/lib/merchant_sidekick/order.rb +244 -0
  25. data/lib/merchant_sidekick/payment.rb +59 -0
  26. data/lib/merchant_sidekick/purchase_invoice.rb +180 -0
  27. data/lib/merchant_sidekick/purchase_order.rb +350 -0
  28. data/lib/merchant_sidekick/railtie.rb +7 -0
  29. data/lib/merchant_sidekick/sales_invoice.rb +56 -0
  30. data/lib/merchant_sidekick/sales_order.rb +122 -0
  31. data/lib/merchant_sidekick/sellable.rb +88 -0
  32. data/lib/merchant_sidekick/seller.rb +93 -0
  33. data/lib/merchant_sidekick/shopping_cart/cart.rb +225 -0
  34. data/lib/merchant_sidekick/shopping_cart/line_item.rb +152 -0
  35. data/lib/merchant_sidekick/version.rb +3 -0
  36. data/merchant_sidekick.gemspec +37 -0
  37. data/spec/address_spec.rb +153 -0
  38. data/spec/addressable_spec.rb +250 -0
  39. data/spec/buyer_spec.rb +203 -0
  40. data/spec/cart_line_item_spec.rb +58 -0
  41. data/spec/cart_spec.rb +213 -0
  42. data/spec/config/merchant_sidekick.yml +10 -0
  43. data/spec/credit_card_payment_spec.rb +175 -0
  44. data/spec/fixtures/addresses.yml +97 -0
  45. data/spec/fixtures/line_items.yml +18 -0
  46. data/spec/fixtures/orders.yml +24 -0
  47. data/spec/fixtures/payments.yml +17 -0
  48. data/spec/fixtures/products.yml +12 -0
  49. data/spec/fixtures/users.yml +11 -0
  50. data/spec/gateway_spec.rb +136 -0
  51. data/spec/invoice_spec.rb +79 -0
  52. data/spec/line_item_spec.rb +65 -0
  53. data/spec/order_spec.rb +85 -0
  54. data/spec/payment_spec.rb +14 -0
  55. data/spec/purchase_invoice_spec.rb +70 -0
  56. data/spec/purchase_order_spec.rb +191 -0
  57. data/spec/sales_invoice_spec.rb +58 -0
  58. data/spec/sales_order_spec.rb +107 -0
  59. data/spec/schema.rb +28 -0
  60. data/spec/sellable_spec.rb +34 -0
  61. data/spec/seller_spec.rb +201 -0
  62. data/spec/spec_helper.rb +255 -0
  63. metadata +201 -0
@@ -0,0 +1,12 @@
1
+ Gemfile.lock
2
+ doc
3
+ docs
4
+ pkg
5
+ .DS_Store
6
+ coverage
7
+ .yardoc
8
+ .rvmrc
9
+ *.gem
10
+ *.sqlite3
11
+ *.rbc
12
+ *.lock
@@ -0,0 +1,38 @@
1
+ # MerchantSidekick Changelog
2
+
3
+ ## 0.4.2 (NOT RELEASED YET)
4
+
5
+ * Renamed state transition exit callbacks for order and invoice
6
+ * Loosened active_record gem dependency to work with Rails 3.2.x
7
+ * Version bump
8
+
9
+ ## 0.4.1 (NOT RELEASED YET)
10
+
11
+ * Fix overridden `build_#{relation}` method signature
12
+ * Fix deprecations on class\_inheritable\_writer
13
+ * Fix deprecations when using Money class with ActiveMerchant
14
+ * Fix authorize\_net\_gateway and paypal_gateway merchant gateways
15
+ * Refactored out active_merchant specific gateways into separate module
16
+ * Removed option to load gateway configuration from database
17
+ * Added gateway spec
18
+ * Allow for MerchantSidekick::Gateway.default_gateway to use type name
19
+ * Refactored default_gateway for active merchant gateway types into base class
20
+ * Added bogus gateway configuration wrapper
21
+ * Cart instances can now be added directly to purchase
22
+ * Cart instances can now be sold
23
+ * Change @customer.purchase syntax to accept :from => @merchant
24
+ * Change @merchant.sell syntax to accept :to => @customer
25
+ * Ignoring blank sellables from purchase and sell
26
+
27
+ ## 0.4.0 (2011-12-27)
28
+
29
+ * Compatibility with ActiveRecord 3.1
30
+ * Refactored code into modules
31
+
32
+ ## 0.0.2 (2009-04-28)
33
+
34
+ * Fixed invoice void, capture and purchase.
35
+
36
+ ## 0.0.1 (2008-08-06)
37
+
38
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2011 Juergen Fesslmeier.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,88 @@
1
+ # MerchantSidekick
2
+
3
+ MerchantSidekick is a light-weight E-commerce framework for Ruby on Rails applications.
4
+
5
+ ## Features
6
+
7
+ MerchantSidekick includes in- and outbound order management and invoicing,
8
+ a shopping cart, taxation and payment processing. It integrates with
9
+ [ActiveMerchant](http://activemerchant.org) for payment processing by default.
10
+ The plugin can be extended to use other payment processors outside the scope
11
+ of ActiveMerchant.
12
+
13
+ ## Quickstart
14
+
15
+ gem install merchant_sidekick
16
+
17
+ rails new my_app
18
+
19
+ cd my_app
20
+
21
+ gem "merchant_sidekick"
22
+
23
+ rails generate merchant_sidekick:install
24
+
25
+ rake db:migrate
26
+
27
+ # edit config/initializers/merchant_sidekick.rb
28
+ MerchantSidekick::default_gateway = :bogus_gateway
29
+
30
+ # edit app/models/user.rb
31
+ class User < ActiveRecord::Base
32
+ acts_as_buyer
33
+ end
34
+
35
+ # edit app/models/product.rb
36
+ class Product < ActiveRecord::Base
37
+ acts_as_sellable
38
+ end
39
+
40
+ rails generate controller orders
41
+
42
+ # edit app/controllers/orders_controller.rb
43
+ class OrdersController < ApplicationController
44
+ before_filter load_cart
45
+ ...
46
+
47
+ def create
48
+ @credit_card = ActiveMerchant::Billing::CreditCard.new(params[:credit_card] || {})
49
+ if @credit_card.valid?
50
+ @order = @user.purchase(@cart)
51
+ @payment = @order.pay(@credit_card)
52
+ redirect_to [@order] and return if @payment.valid?
53
+ end
54
+ render :template => "new"
55
+ end
56
+
57
+ protected
58
+
59
+ def load_cart
60
+ @cart = YAML.load(session[:cart].to_s)
61
+ end
62
+
63
+ ...
64
+
65
+ end
66
+
67
+
68
+ ## License
69
+
70
+ Copyright (c) 2008-2012 Juergen Fesslmeier, released under the MIT license.
71
+
72
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
73
+ this software and associated documentation files (the "Software"), to deal in
74
+ the Software without restriction, including without limitation the rights to
75
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
76
+ of the Software, and to permit persons to whom the Software is furnished to do
77
+ so, subject to the following conditions:
78
+
79
+ The above copyright notice and this permission notice shall be included in all
80
+ copies or substantial portions of the Software.
81
+
82
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
83
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
84
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
85
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
86
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
87
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
88
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require "rubygems"
2
+ require "rake/testtask"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
7
+ t.pattern = 'spec/**/*_spec.rb'
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,45 @@
1
+ require "active_record"
2
+ require "active_support/core_ext" # TODO remove once we replace inheritable_attribute readers with configurations
3
+ require "money"
4
+ require "merchant_sidekick/version"
5
+ require "merchant_sidekick/money"
6
+ require "acts_as_list"
7
+ require "aasm"
8
+ require "active_merchant"
9
+
10
+ ActiveRecord::Base.extend MerchantSidekick::Money
11
+
12
+ require 'merchant_sidekick/addressable/addressable'
13
+ require 'merchant_sidekick/addressable/address'
14
+ ActiveRecord::Base.send(:include, MerchantSidekick::Addressable)
15
+
16
+ require 'merchant_sidekick/sellable'
17
+ require 'merchant_sidekick/buyer'
18
+ require 'merchant_sidekick/seller'
19
+
20
+ require 'merchant_sidekick/line_item'
21
+ require 'merchant_sidekick/order'
22
+ require 'merchant_sidekick/purchase_order'
23
+ require 'merchant_sidekick/sales_order'
24
+ require 'merchant_sidekick/invoice'
25
+ require 'merchant_sidekick/purchase_invoice'
26
+ require 'merchant_sidekick/sales_invoice'
27
+
28
+ ActiveRecord::Base.send(:include, MerchantSidekick::Sellable)
29
+ ActiveRecord::Base.send(:include, MerchantSidekick::Buyer)
30
+ ActiveRecord::Base.send(:include, MerchantSidekick::Seller)
31
+
32
+ require 'merchant_sidekick/shopping_cart/cart'
33
+ require 'merchant_sidekick/shopping_cart/line_item'
34
+
35
+ require 'merchant_sidekick/gateway'
36
+ require 'merchant_sidekick/payment'
37
+
38
+ require 'merchant_sidekick/active_merchant/credit_card_payment'
39
+
40
+ require 'merchant_sidekick/active_merchant/gateways/base'
41
+ require 'merchant_sidekick/active_merchant/gateways/bogus_gateway'
42
+ require 'merchant_sidekick/active_merchant/gateways/authorize_net_gateway'
43
+ require 'merchant_sidekick/active_merchant/gateways/paypal_gateway'
44
+
45
+ require 'merchant_sidekick/railtie' if defined?(Rails)
@@ -0,0 +1,117 @@
1
+ # This class handles all credit card payment transactions using ActiveMerchant.
2
+ module MerchantSidekick
3
+ module ActiveMerchant
4
+ class CreditCardPayment < Payment
5
+ cattr_accessor :gateway
6
+ serialize :params
7
+
8
+ class << self
9
+
10
+ # Returns an active merchant gateway instance, based on the following:
11
+ #
12
+ # * an active merchant gateway instance assigned to the gateway
13
+ # class accessor
14
+ # * a merchant sidekick specific gateway identifier,
15
+ # e.g. :authorize_net_gateway, is passed into the gateway class accessor
16
+ # * otherwise falls back to the default gateway assigned as
17
+ # MerchantSidekick::Gateways::Gateway.default_gateway
18
+ #
19
+ # Declare as needed in the environment.
20
+ #
21
+ # E.g.
22
+ #
23
+ # CreditCardPayment.gateway = ActiveMerchant::Billing::AuthorizeNetGateway.new({
24
+ # :login => @login_id,
25
+ # :password => @transaction_key
26
+ # })
27
+ #
28
+ # or
29
+ #
30
+ # CreditCardPayment.gateway = :authorize_net_gateway
31
+ #
32
+ def gateway
33
+ if @@gateway.is_a? ::ActiveMerchant::Billing::Gateway
34
+ @@gateway
35
+ elsif @@gateway.is_a? Symbol
36
+ @@gateway = "::MerchantSidekick::ActiveMerchant::Gateways::#{@@gateway.to_s.classify}".constantize.gateway
37
+ @@gateway
38
+ else
39
+ @@gateway = MerchantSidekick::Gateway.default_gateway
40
+ end
41
+ end
42
+
43
+ def authorize(amount, credit_card, options = {})
44
+ process('authorization', amount, options) do |gw|
45
+ gw.authorize(amount.cents, credit_card, options)
46
+ end
47
+ end
48
+
49
+ def capture(amount, authorization, options = {})
50
+ process('capture', amount, options) do |gw|
51
+ gw.capture(amount.cents, authorization, options)
52
+ end
53
+ end
54
+
55
+ def purchase(amount, credit_card, options = {})
56
+ process('purchase', amount, options) do |gw|
57
+ gw.purchase(amount.cents, credit_card, options)
58
+ end
59
+ end
60
+
61
+ def void(amount, authorization, options = {})
62
+ process('void', amount, options) do |gw|
63
+ gw.void(authorization, options)
64
+ end
65
+ end
66
+
67
+ # requires :card_number option
68
+ def credit(amount, authorization, options = {})
69
+ process('credit', amount, options) do |gw|
70
+ gw.credit(amount.cents, authorization, options)
71
+ end
72
+ end
73
+
74
+ # works with paypal payflow
75
+ def transfer(amount, destination_account, options={})
76
+ process('transfer', amount, options) do |gw|
77
+ gw.transfer(amount.cents, destination_account, options)
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def process(action, amount = nil, options = {})
84
+ result = CreditCardPayment.new
85
+ result.amount = amount
86
+ result.action = action
87
+
88
+ begin
89
+ options[:currency] = amount.currency if amount.respond_to?(:currency)
90
+ response = yield gateway
91
+
92
+ result.success = response.success?
93
+ result.reference = response.authorization
94
+ result.message = response.message
95
+ result.params = response.params
96
+ result.test = response.test?
97
+ rescue ::ActiveMerchant::ActiveMerchantError => e
98
+ result.success = false
99
+ result.reference = nil
100
+ result.message = e.message
101
+ result.params = {}
102
+ result.test = gateway.test?
103
+ end
104
+ result
105
+ end
106
+ end
107
+
108
+ #--- instance methods
109
+
110
+ # override in sublcass
111
+ def payment_type
112
+ :credit_card
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,26 @@
1
+ # Implements the AuthorizeNet specific active merchant gateway wrapper
2
+ # to configure the gateway properly.
3
+ module MerchantSidekick
4
+ module ActiveMerchant
5
+ module Gateways
6
+ class AuthorizeNetGateway < ::MerchantSidekick::ActiveMerchant::Gateways::Base
7
+ class << self
8
+
9
+ # Returns an active merchant authorize net gateway instance
10
+ # unless there is already one assigned.
11
+ def gateway
12
+ unless @@gateway
13
+ ::ActiveMerchant::Billing::Base.mode = :test if config[:mode] == "test"
14
+ @@gateway = ::ActiveMerchant::Billing::AuthorizeNetGateway.new({
15
+ :login => config[:login_id],
16
+ :password => config[:transaction_key]
17
+ }.merge(config[:mode] == "test" ? {:test => true} : {}))
18
+ end
19
+ @@gateway
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # Implements the base gateway for all active merchant gateways
2
+ module MerchantSidekick
3
+ class << self
4
+
5
+ # Overrides MerchantSidekick::default_gateway setter defined in merchant_sidekick/gateway
6
+ def default_gateway=(value)
7
+ ::MerchantSidekick::ActiveMerchant::Gateways::Base.default_gateway = value
8
+ end
9
+ end
10
+
11
+ module ActiveMerchant
12
+
13
+ module Gateways
14
+ class Base < MerchantSidekick::Gateway
15
+ class << self
16
+
17
+ def default_gateway=(value)
18
+ if value.is_a?(Symbol)
19
+ super "::MerchantSidekick::ActiveMerchant::Gateways::#{value.to_s.classify}".constantize.gateway
20
+ else
21
+ super value
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # Implements the bogus gatway active merchant gateway wrapper
2
+ module MerchantSidekick
3
+ module ActiveMerchant
4
+ module Gateways
5
+ class BogusGateway < ::MerchantSidekick::ActiveMerchant::Gateways::Base
6
+ class << self
7
+
8
+ def gateway
9
+ unless @@gateway
10
+ @@gateway = ::ActiveMerchant::Billing::BogusGateway.new
11
+ end
12
+ @@gateway
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ # Implements the Paypal Website Payments Pro specific active merchant gateway wrapper
2
+ # to configure the gateway properly.
3
+ module MerchantSidekick
4
+ module ActiveMerchant
5
+ module Gateways
6
+ class PaypalGateway < ::MerchantSidekick::ActiveMerchant::Gateways::Base
7
+ class << self
8
+
9
+ # Returns an active merchant paypal gateway instance
10
+ #
11
+ # E.g.
12
+ #
13
+ # # config/active_merchant.yml
14
+ # development:
15
+ # api_username: seller_XYZ_biz_api1.example.com
16
+ # api_password: ABCDEFG123456789
17
+ # signature: AsPC9BjkCyDFQXbStoZcgqH3hpacAX3IenGazd35.nEnXJKR9nfCmJDu
18
+ # pem_file_name: config/paypal.pem
19
+ # mode: test
20
+ # production:
21
+ # ...
22
+ #
23
+ def gateway
24
+ unless @@gateway
25
+ options = {
26
+ :login => config[:api_username],
27
+ :password => config[:api_password]
28
+ }
29
+ options.merge!({:test => true}) if config[:mode] == "test"
30
+ options.merge!({:signature => config[:signature]}) unless config[:signature].blank?
31
+
32
+ ::ActiveMerchant::Billing::Base.mode = :test if config[:mode] == "test"
33
+ ::ActiveMerchant::Billing::PaypalGateway.pem_file = File.read(File.expand_path(config[:pem_file_name])) if config[:pem_file_name]
34
+ @@gateway = ::ActiveMerchant::Billing::PaypalGateway.new(options)
35
+ end
36
+ @@gateway
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end