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,58 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe MerchantSidekick::SalesInvoice do
4
+
5
+ def setup
6
+ @buyer = users(:sam)
7
+ @seller = users(:sally)
8
+ @product = products(:widget)
9
+ @invoice = MerchantSidekick::SalesInvoice.new(
10
+ :net_amount => Money.new(2995, 'USD'),
11
+ :tax_amount => Money.new(0, 'USD'),
12
+ :gross_amount => Money.new(2995, 'USD'),
13
+ :buyer => @buyer,
14
+ :seller => @seller
15
+ )
16
+
17
+ # addresses
18
+ @sally_billing = @invoice.build_origin_address(addresses(:sally_billing).content_attributes)
19
+ @sam_billing = @invoice.build_billing_address(addresses(:sam_billing).content_attributes)
20
+ @sam_shipping = @invoice.build_shipping_address(addresses(:sam_shipping).content_attributes)
21
+
22
+ # line items and add
23
+ @line_item = MerchantSidekick::LineItem.new(
24
+ :order => nil,
25
+ :invoice => @invoice,
26
+ :sellable => @product,
27
+ :tax_rate => 0
28
+ )
29
+ @line_item.should_not be_nil
30
+ @invoice.line_items.push(@line_item)
31
+
32
+ # credit card
33
+ @credit_card = valid_credit_card
34
+ end
35
+
36
+ it "should cash" do
37
+ transaction do
38
+ lambda {
39
+ payment = @invoice.cash("success@test.tst")
40
+ payment.should be_success
41
+ payment.position.should == 1
42
+ @invoice.current_state.should == :paid
43
+ }.should change(MerchantSidekick::Payment, :count).by(1)
44
+ end
45
+ end
46
+
47
+ it "should not cash" do
48
+ transaction do
49
+ lambda {
50
+ payment = @invoice.cash("error@error.tst")
51
+ payment.should_not be_success
52
+ payment.position.should == 1
53
+ @invoice.current_state.should == :payment_declined
54
+ }.should change(MerchantSidekick::Payment, :count).by(1)
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,107 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe MerchantSidekick::SalesOrder, "with valid account" do
4
+
5
+ def setup
6
+ @sam = users(:sam)
7
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
8
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
9
+
10
+ @user = @sally = users(:sally)
11
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
12
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
13
+
14
+ @product = products(:widget)
15
+ @order = @sally.sell_to @sam, @product
16
+ @account = "pass@test.tst"
17
+
18
+ # ActiveMerchant::Billing::Base.mode = :test
19
+ end
20
+
21
+ it "should return a payment" do
22
+ transaction do
23
+ @order.cash(@account).should be_instance_of(MerchantSidekick::ActiveMerchant::CreditCardPayment)
24
+ end
25
+ end
26
+
27
+ it "should return success" do
28
+ transaction do
29
+ @order.cash(@account).should be_success
30
+ end
31
+ end
32
+
33
+ it "should be set to state pending" do
34
+ transaction do
35
+ @order.cash(@account)
36
+ @order.should be_approved
37
+ end
38
+ end
39
+
40
+ it "should have a valid invoice" do
41
+ transaction do
42
+ @order.cash(@account)
43
+ @order.invoice.should_not be_nil
44
+ @order.invoice_id.should_not be_nil
45
+ @order.invoice.should be_instance_of(MerchantSidekick::SalesInvoice)
46
+ @order.invoice.should be_paid
47
+ end
48
+ end
49
+
50
+ it "should set payment amount equal to order amount" do
51
+ transaction do
52
+ @order.cash(@account).amount.should == @order.total
53
+ end
54
+ end
55
+
56
+ it "should have corret addresses" do
57
+ transaction do
58
+ @order.origin_address.to_s.should_not be_blank
59
+ @order.origin_address.to_s.should == @sally.billing_address.to_s
60
+
61
+ @order.billing_address.to_s.should_not be_blank
62
+ @order.billing_address.to_s.should == @sam.billing_address.to_s
63
+
64
+ @order.shipping_address.to_s.should_not be_blank
65
+ @order.shipping_address.to_s.should == @sam.shipping_address.to_s
66
+
67
+ @order.cash(@account).should be_success
68
+
69
+ @order.invoice.origin_address.to_s.should_not be_blank
70
+ @order.invoice.origin_address.to_s.should == @sally.billing_address.to_s
71
+
72
+ @order.invoice.billing_address.to_s.should_not be_blank
73
+ @order.invoice.billing_address.to_s.should == @sam.billing_address.to_s
74
+
75
+ @order.invoice.shipping_address.to_s.should_not be_blank
76
+ @order.invoice.shipping_address.to_s.should == @sam.shipping_address.to_s
77
+ end
78
+ end
79
+
80
+ describe MerchantSidekick::SalesOrder, "with invalid account" do
81
+
82
+ def setup
83
+ @sam = users(:sam)
84
+ @product = products(:widget)
85
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
86
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
87
+
88
+ @user = @sally = users(:sally)
89
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
90
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
91
+
92
+ @order = @sally.sell_to @sam, @product
93
+ @payment = @order.cash 'error@error.tst'
94
+ end
95
+
96
+ it "should initialize and save the order and invoice" do
97
+ transaction do
98
+ @order.should be_is_a(MerchantSidekick::SalesOrder)
99
+ @order.should_not be_new_record
100
+ @order.invoice.should be_payment_declined
101
+ @order.should be_created
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,28 @@
1
+ require "merchant_sidekick/migrations/addressable"
2
+ require "merchant_sidekick/migrations/billing"
3
+ require "merchant_sidekick/migrations/shopping_cart"
4
+
5
+ CreateMerchantSidekickAddressableTables.up
6
+ CreateMerchantSidekickBillingTables.up
7
+ CreateMerchantSidekickShoppingCartTables.up
8
+
9
+ ActiveRecord::Schema.define :version => 0 do
10
+ create_table :addressables, :force => true do |t|
11
+ t.column :name, :string
12
+ t.column :type, :string
13
+ end
14
+
15
+ create_table :products, :force => true do |t|
16
+ t.column :title, :string
17
+ t.column :description, :text
18
+ t.column :type, :string
19
+ t.column :price_cents, :integer, :null => false, :default => 0
20
+ t.column :price_currency, :string, :null => false, :default => "USD"
21
+ end
22
+
23
+ create_table :users, :force => true do |t|
24
+ t.column :name, :string
25
+ t.column :email, :string
26
+ t.column :type, :string
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe "A sellable model" do
4
+
5
+ it "should have a price" do
6
+ lambda { Product.new.should.respond_to?(:price) }.should_not raise_error
7
+ end
8
+
9
+ it "should have many line_items" do
10
+ lambda { Product.new.line_items.should == [] }.should_not raise_error
11
+ end
12
+
13
+ it "should have many orders" do
14
+ lambda { Product.new.orders.should == [] }.should_not raise_error
15
+ end
16
+
17
+ end
18
+
19
+ describe "A sellable with money" do
20
+
21
+ it "should have a currency accessor" do
22
+ lambda {
23
+ Product.new.should.respond_to?(:price_cents)
24
+ Product.new.should.respond_to?(:price_currency)
25
+ Product.new.price_currency.should == "USD"
26
+ Product.new.currency.should == ::Money::Currency.wrap("USD")
27
+ }.should_not raise_error
28
+ end
29
+
30
+ it "should have a currency as string accessor" do
31
+ lambda { Product.new.currency_as_string.should == "USD" }.should_not raise_error
32
+ end
33
+
34
+ end
@@ -0,0 +1,201 @@
1
+ require File.expand_path("../spec_helper", __FILE__)
2
+
3
+ describe "A seller's model" do
4
+
5
+ it "should be able to sell" do
6
+ SellingUser.new.should respond_to(:sell)
7
+ end
8
+
9
+ it "should be able to sell" do
10
+ SellingUser.new.should respond_to(:sell_to)
11
+ end
12
+
13
+ it "should have many orders" do
14
+ lambda { SellingUser.new.orders(true).first }.should_not raise_error
15
+ end
16
+
17
+ it "should have many invoices" do
18
+ lambda { SellingUser.new.invoices(true).first }.should_not raise_error
19
+ end
20
+
21
+ it "should have many sales orders" do
22
+ lambda { SellingUser.new.sales_orders(true).first }.should_not raise_error
23
+ end
24
+
25
+ it "should have many sales invoices" do
26
+ lambda { SellingUser.new.sales_invoices(true).first }.should_not raise_error
27
+ end
28
+
29
+ end
30
+
31
+ describe "A seller sells a product" do
32
+
33
+ def setup
34
+ @sally = users(:sally)
35
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
36
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
37
+ @sam = users(:sam)
38
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
39
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
40
+ @product = products(:widget)
41
+ end
42
+
43
+ it "should create a new order" do
44
+ transaction do
45
+ lambda do
46
+ order = @sally.sell_to @sam, @product
47
+ order.should be_an_instance_of(MerchantSidekick::SalesOrder)
48
+ order.buyer.should == @sam
49
+ order.should be_valid
50
+ order.save!
51
+ end.should change(MerchantSidekick::Order, :count)
52
+ end
53
+ end
54
+
55
+ it "should build sales order :to option" do
56
+ transaction do
57
+ lambda do
58
+ order = @sally.sell @product, :to => @sam
59
+ order.should be_an_instance_of(MerchantSidekick::SalesOrder)
60
+ order.seller.should == @sally
61
+ order.buyer.should == @sam
62
+ order.should be_valid
63
+ order.save!
64
+ end.should change(MerchantSidekick::Order, :count)
65
+ end
66
+ end
67
+
68
+ it "should add to seller's orders" do
69
+ transaction do
70
+ order = @sally.sell_to(@sam, @product)
71
+ order.save!
72
+ @sally.orders.last.should == order
73
+ @sally.sales_orders.last.should == order
74
+ order.seller.should == @sally
75
+ order.buyer.should == @sam
76
+ end
77
+ end
78
+
79
+ it "should create line items" do
80
+ transaction do
81
+ order = @sally.sell_to(@sam, @product)
82
+ order.line_items.size.should == 1
83
+ order.line_items.first.sellable.should == @product
84
+ end
85
+ end
86
+
87
+ it "should set line item amount to sellable price" do
88
+ transaction do
89
+ order = @sally.sell_to @sam, @product
90
+ order.line_items.first.amount.should == @product.price
91
+ end
92
+ end
93
+
94
+ it "should set line item amount to 0 if sellable does not have a price" do
95
+ transaction do
96
+ @product.price = 0
97
+ order = @sally.sell_to @sam, @product
98
+ order.line_items.first.amount.should == 0.to_money
99
+ end
100
+ end
101
+
102
+ it "should ignore blank sellables" do
103
+ transaction do
104
+ order = @sally.sell_to @sam, @product, nil
105
+ order.line_items.size.should == 1
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ describe "A seller selling multiple products" do
112
+
113
+ def setup
114
+ @sally = users(:sally)
115
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
116
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
117
+ @sam = users(:sam)
118
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
119
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
120
+ @products = [products(:widget), products(:knob)]
121
+ @order = @sally.sell_to @sam, @products
122
+ end
123
+
124
+ it "should create line items for each sellable" do
125
+ transaction do
126
+ lambda { @order.save! }.should change(MerchantSidekick::LineItem, :count).by(2)
127
+ @order.should have(2).line_items
128
+ @order.line_items.map(&:sellable).should == @products
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "A seller selling a cart" do
134
+
135
+ def setup
136
+ @sally = users(:sally)
137
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
138
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
139
+ @sam = users(:sam)
140
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
141
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
142
+ @products = [products(:widget), products(:knob)]
143
+ @order = @sally.sell_to @sam, @products
144
+ @cart = MerchantSidekick::ShoppingCart::Cart.new
145
+ end
146
+
147
+ it "should sell one cart" do
148
+ transaction do
149
+ @cart.add(@products)
150
+ @cart.total.to_s.should == "33.94"
151
+ order = @sally.sell_to @sam, @cart
152
+ order.total.to_s.should == "33.94"
153
+ end
154
+ end
155
+
156
+ it "should sell multipe carts" do
157
+ transaction do
158
+ cart1 = MerchantSidekick::ShoppingCart::Cart.new
159
+ cart2 = MerchantSidekick::ShoppingCart::Cart.new
160
+
161
+ cart1.add(@products.first)
162
+ cart2.add(@products.last)
163
+ order = @sally.sell_to @sam, cart1, cart2
164
+ order.total.to_s.should == "33.94"
165
+ end
166
+ end
167
+
168
+ end
169
+
170
+
171
+ describe "A seller selling no product" do
172
+
173
+ def setup
174
+ @sally = users(:sally)
175
+ @sally_billing = @sally.create_billing_address(addresses(:sally_billing).content_attributes)
176
+ @sally_shipping = @sally.create_shipping_address(addresses(:sally_shipping).content_attributes)
177
+ @sam = users(:sam)
178
+ @sam_billing = @sam.create_billing_address(addresses(:sam_billing).content_attributes)
179
+ @sam_shipping = @sam.create_shipping_address(addresses(:sam_shipping).content_attributes)
180
+ @product = products(:widget)
181
+ end
182
+
183
+ it "should raise an error as sell is a protected method" do
184
+ transaction do
185
+ lambda { @sally.sell(@product) }.should raise_error(NoMethodError)
186
+ end
187
+ end
188
+
189
+ it "should raise an error for there is no sellable" do
190
+ transaction do
191
+ lambda { @sally.sell_to(@sam) }.should raise_error(ArgumentError)
192
+ end
193
+ end
194
+
195
+ it "should raise an error for no sellable" do
196
+ transaction do
197
+ lambda { @sally.sell_to(@sam, []) }.should raise_error(ArgumentError)
198
+ end
199
+ end
200
+
201
+ end
@@ -0,0 +1,255 @@
1
+ require "bundler/setup"
2
+ require "rspec"
3
+ require "active_record"
4
+ require "active_support"
5
+ require "sqlite3"
6
+ require "merchant_sidekick"
7
+
8
+ RSpec.configure do |config|
9
+ # config.use_transactional_fixtures = true
10
+ # config.use_instantiated_fixtures = false
11
+ # config.fixture_path = File.dirname(__FILE__) + '/fixtures'
12
+ end
13
+
14
+ # If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
15
+ if ENV["LOG"]
16
+ require "logger"
17
+ ActiveRecord::Base.logger = Logger.new($stdout)
18
+ end
19
+
20
+ # Provide basic Rails methods for testing purposes
21
+ unless defined?(Rails)
22
+ module Rails
23
+ extend self
24
+ def env; "test"; end
25
+ def root; Pathname.new(File.expand_path("..", __FILE__)); end
26
+ end
27
+ end
28
+
29
+ ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
30
+ ActiveRecord::Migration.verbose = false
31
+
32
+ require "schema"
33
+ at_exit {ActiveRecord::Base.connection.disconnect!}
34
+
35
+ Money.default_currency = Money::Currency.wrap("USD")
36
+
37
+ #--- Sudo fixtures
38
+
39
+ def transaction
40
+ ActiveRecord::Base.connection.transaction do
41
+ send(:setup) if respond_to?(:setup)
42
+ yield
43
+ raise ActiveRecord::Rollback
44
+ end
45
+ end
46
+
47
+ def users(key, options = {})
48
+ values = YAML::load_file(File.expand_path("../fixtures/users.yml", __FILE__))
49
+ (values[key.to_s]["type"] || "User").constantize.create! values[key.to_s].merge(options)
50
+ end
51
+
52
+ def products(key, options = {})
53
+ values = YAML::load_file(File.expand_path("../fixtures/products.yml", __FILE__))
54
+ (values[key.to_s]["type"] || "Product").constantize.create! values[key.to_s].merge(options)
55
+ end
56
+
57
+ def addresses(key, options = {})
58
+ values = YAML::load_file(File.expand_path("../fixtures/addresses.yml", __FILE__))
59
+ (values[key.to_s]["type"] || "MerchantSidekick::Addressable::Address").constantize.create! values[key.to_s].merge(options)
60
+ end
61
+
62
+ def orders(key, options = {})
63
+ values = YAML::load_file(File.expand_path("../fixtures/orders.yml", __FILE__))
64
+ (values[key.to_s]["type"] || "MerchantSidekick::Order").constantize.create! values[key.to_s].merge(options)
65
+ end
66
+
67
+ def payments(key, options = {})
68
+ values = YAML::load_file(File.expand_path("../fixtures/payments.yml", __FILE__))
69
+ (values[key.to_s]["type"] || "MerchantSidekick::Payment").constantize.create! values[key.to_s].merge(options)
70
+ end
71
+
72
+ def line_items(key, options = {})
73
+ values = YAML::load_file(File.expand_path("../fixtures/line_items.yml", __FILE__))
74
+ (values[key.to_s]["type"] || "MerchantSidekick::LineItem").constantize.create! values[key.to_s].merge(options)
75
+ end
76
+
77
+ #--- MerchantSidekick::Addressable test models
78
+
79
+ class MerchantSidekick::Addressable::Address
80
+ # extends to_s to add name for testing purposes
81
+ def to_s_with_name
82
+ name = []
83
+ name << self.first_name
84
+ name << self.middle_name if MerchantSidekick::Addressable::Address.middle_name?
85
+ name << self.last_name
86
+ name = name.reject(&:blank?).join(" ")
87
+ [name, to_s_without_name].reject(&:blank?).join(", ")
88
+ end
89
+ alias_method_chain :to_s, :name
90
+ end
91
+
92
+ class Addressable < ActiveRecord::Base; end
93
+
94
+ class HasOneSingleAddressModel < Addressable
95
+ has_address
96
+ end
97
+
98
+ class HasManySingleAddressModel < Addressable
99
+ has_addresses
100
+ end
101
+
102
+ class HasOneMultipleAddressModel < Addressable
103
+ has_address :billing, :shipping
104
+ end
105
+
106
+ class HasManyMultipleAddressModel < Addressable
107
+ has_addresses :billing, :shipping
108
+ end
109
+
110
+ def valid_address_attributes(attributes = {})
111
+ {
112
+ :first_name => "George",
113
+ :last_name => "Bush",
114
+ :gender => 'm',
115
+ :street => "100 Washington St.",
116
+ :postal_code => "95065",
117
+ :city => "Santa Cruz",
118
+ :province_code => "CA",
119
+ :province => "California",
120
+ :company_name => "Exxon",
121
+ :phone => "+1 831 123-4567",
122
+ :mobile => "+1 831 223-4567",
123
+ :fax => "+1 831 323-4567",
124
+ :country_code => "US",
125
+ :country => "United States of America"
126
+ }.merge(MerchantSidekick::Addressable::Address.middle_name? ? { :middle_name => "W." } : {}).merge(attributes)
127
+ end
128
+
129
+ #--- MerchantSidekick generic test models
130
+
131
+ class Product < ActiveRecord::Base
132
+ money :price, :cents => :price_cents, :currency => :price_currency
133
+ acts_as_sellable
134
+
135
+ # TODO weird cart serialization workaround
136
+ def target; true; end
137
+ end
138
+
139
+ class ProductWithNameAndSku < Product
140
+ def name; "A beautiful name"; end
141
+ def sku; "PR1234"; end
142
+ def description; "Wonderful name!"; end
143
+ def taxable; true; end
144
+ end
145
+
146
+ class ProductWithTitleAndNumber < Product
147
+ def title; "A beautiful title"; end
148
+ def number; "PR1234"; end
149
+ def description; "Wonderful title!"; end
150
+ def new_record?; true; end
151
+ end
152
+
153
+ class ProductWithCopy < Product
154
+ def copy_name(options={}); "customized name"; end
155
+ def copy_item_number(options = {}); "customized item number"; end
156
+ def copy_description(options = {}); "customized description"; end
157
+ def copy_price(options = {}); Money.new(9999, "USD"); end
158
+ end
159
+
160
+ class User < ActiveRecord::Base
161
+ has_address :billing, :shipping
162
+ end
163
+
164
+ # TODO rename to just "Buyer"
165
+ class BuyingUser < User
166
+ acts_as_buyer
167
+ end
168
+
169
+ # TODO rename to just "Seller"
170
+ class SellingUser < User
171
+ acts_as_seller
172
+ end
173
+
174
+ #--- MerchantSidekick shopping cart
175
+
176
+ def valid_cart_line_item_attributes(attributes = {})
177
+ {:quantity => 5}.merge(attributes)
178
+ end
179
+
180
+ #--- ActiveMerchant related helpers
181
+
182
+ def valid_credit_card_attributes(attributes = {})
183
+ {
184
+ :number => "1", #"4242424242424242",
185
+ :first_name => "Claudio",
186
+ :last_name => "Almende",
187
+ :month => "8",
188
+ :year => "#{ Time.now.year + 1 }",
189
+ :verification_value => '123',
190
+ :type => 'visa'
191
+ }.merge(attributes)
192
+ end
193
+
194
+ def invalid_credit_card_attributes(attributes = {})
195
+ {
196
+ :first_name => "Bad",
197
+ :last_name => "Boy",
198
+ :month => "8",
199
+ :year => Time.now.year + 1,
200
+ :number => "2",
201
+ :type => "bogus"
202
+ }.merge(attributes)
203
+ end
204
+
205
+ def credit_card(options={})
206
+ ActiveMerchant::Billing::CreditCard.new(valid_credit_card_attributes(options))
207
+ end
208
+
209
+ def valid_credit_card(options={})
210
+ credit_card(valid_credit_card_attributes(options))
211
+ end
212
+
213
+ def invalid_credit_card(options={})
214
+ credit_card(invalid_credit_card_attributes(options))
215
+ end
216
+
217
+ module ActiveMerchant
218
+ module Billing
219
+ class BogusGateway < Gateway
220
+
221
+ # Transfers money to one or multiple recipients (bulk transfer).
222
+ #
223
+ # Overloaded activemerchant bogus gateways to support transfers, similar
224
+ # to Paypal Website Payments Pro functionality.
225
+ #
226
+ # E.g.
227
+ #
228
+ # @gateway.transfer 1000, "bob@example.com",
229
+ # :subject => "The money I owe you", :note => "Sorry, it's coming in late."
230
+ #
231
+ # gateway.transfer [1000, 'fred@example.com'],
232
+ # [2450, 'wilma@example.com', :note => 'You will receive an extra payment on March 24.'],
233
+ # [2000, 'barney@example.com'],
234
+ # :subject => "Salary January.", :note => "Thanks for your hard work."
235
+
236
+ def transfer(money, paypal_account, options={})
237
+ if paypal_account == 'fail@error.tst'
238
+ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money.to_s, :error => FAILURE_MESSAGE },:test => true)
239
+ elsif paypal_account == 'error@error.tst'
240
+ raise Error, ERROR_MESSAGE
241
+ elsif /[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$/i.match(paypal_account)
242
+ Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money.to_s}, :test => true)
243
+ else
244
+ raise Error, ERROR_MESSAGE
245
+ end
246
+ end
247
+
248
+ end
249
+ end
250
+ end
251
+
252
+ ActiveMerchant::Billing::Base.mode = :test
253
+ ActiveMerchant::Billing::CreditCard.require_verification_value = true
254
+
255
+ MerchantSidekick::default_gateway = :bogus_gateway