merchant_sidekick 0.4.2

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.
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