gimme_gimme 0.2.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.
- data/Gemfile +3 -0
- data/Gemfile.lock +143 -0
- data/README.md +37 -0
- data/Rakefile +10 -0
- data/features/run_features.feature +40 -0
- data/features/step_definitions/rails_steps.rb +62 -0
- data/features/support/env.rb +2 -0
- data/features/support/file_helpers.rb +11 -0
- data/lib/generators/gimme_gimme/base.rb +17 -0
- data/lib/generators/gimme_gimme/install/install_generator.rb +34 -0
- data/lib/generators/gimme_gimme/install/templates/controllers/credit_cards_controller.rb +3 -0
- data/lib/generators/gimme_gimme/install/templates/create_gimme_gimme_tables.rb +29 -0
- data/lib/generators/gimme_gimme/install/templates/models/credit_card.rb +3 -0
- data/lib/generators/gimme_gimme/test_support/templates/factories/factories.rb +10 -0
- data/lib/generators/gimme_gimme/test_support/templates/support/fake_cim.rb +9 -0
- data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_gateway.rb +15 -0
- data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_server.rb +329 -0
- data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_spec_helpers.rb +21 -0
- data/lib/generators/gimme_gimme/test_support/test_support_generator.rb +23 -0
- data/lib/gimme_gimme.rb +7 -0
- data/lib/gimme_gimme/cim_gateway.rb +16 -0
- data/lib/gimme_gimme/credit_card.rb +125 -0
- data/lib/gimme_gimme/credit_cards_controller.rb +18 -0
- data/lib/gimme_gimme/engine.rb +7 -0
- data/lib/gimme_gimme/user.rb +43 -0
- data/lib/gimme_gimme/version.rb +3 -0
- data/spec/environment.rb +94 -0
- data/spec/factories/clearance.rb +8 -0
- data/spec/models/credit_card_spec.rb +219 -0
- data/spec/models/user_spec.rb +20 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/testapp/config/routes.rb +4 -0
- metadata +226 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module GimmeGimme
|
2
|
+
module CreditCardsController
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_filter :authenticate
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def destroy
|
11
|
+
@user = current_user
|
12
|
+
credit_card = @user.credit_cards.find(params[:id])
|
13
|
+
|
14
|
+
credit_card.destroy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module GimmeGimme
|
2
|
+
module User
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :credit_cards
|
7
|
+
before_validation :generate_customer_key, :create_cim_customer_profile_id, :on => :create
|
8
|
+
validates_presence_of :remote_customer_profile_id
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def credit_card_count
|
13
|
+
self.credit_cards.count
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_cim_customer_profile_id
|
17
|
+
response = self.class.gateway.create_customer_profile(
|
18
|
+
:profile => { :merchant_customer_id => customer_key,
|
19
|
+
:description => description,
|
20
|
+
:email => email }
|
21
|
+
)
|
22
|
+
self.remote_customer_profile_id = response.authorization
|
23
|
+
end
|
24
|
+
private :create_cim_customer_profile_id
|
25
|
+
|
26
|
+
def generate_customer_key
|
27
|
+
unhashed_key = [Time.now.to_s, rand].join('--')
|
28
|
+
self.customer_key = Digest::MD5.hexdigest(unhashed_key).slice(0, 20)
|
29
|
+
end
|
30
|
+
private :generate_customer_key
|
31
|
+
|
32
|
+
def description
|
33
|
+
email
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
def gateway
|
39
|
+
::GimmeGimme::CimGateway.instance.gateway
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/environment.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
PROJECT_ROOT = File.expand_path('../..', __FILE__)
|
2
|
+
$LOAD_PATH << File.join(PROJECT_ROOT)
|
3
|
+
$LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
|
4
|
+
|
5
|
+
require 'rails/all'
|
6
|
+
require 'gimme_gimme'
|
7
|
+
require 'clearance'
|
8
|
+
require 'factory_girl'
|
9
|
+
require 'bourne'
|
10
|
+
require 'spec/factories/clearance'
|
11
|
+
require 'lib/generators/gimme_gimme/test_support/templates/factories/factories'
|
12
|
+
require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_gateway'
|
13
|
+
require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_server'
|
14
|
+
require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_spec_helpers'
|
15
|
+
require 'lib/generators/gimme_gimme/install/templates/create_gimme_gimme_tables'
|
16
|
+
|
17
|
+
class User < ActiveRecord::Base
|
18
|
+
include Clearance::User
|
19
|
+
include GimmeGimme::User
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
class CreditCard < ActiveRecord::Base
|
24
|
+
include GimmeGimme::CreditCard
|
25
|
+
end
|
26
|
+
|
27
|
+
class ApplicationController < ActionController::Base
|
28
|
+
include Clearance::Authentication
|
29
|
+
end
|
30
|
+
|
31
|
+
module Testapp
|
32
|
+
class Application < Rails::Application
|
33
|
+
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
|
34
|
+
config.encoding = 'utf-8'
|
35
|
+
config.paths.config.database = 'spec/testapp/config/database.yml'
|
36
|
+
config.paths.app.models << 'lib/generators/gimme_gimme/install/templates/models'
|
37
|
+
config.paths.config.routes << 'spec/testapp/config/routes.rb'
|
38
|
+
config.paths.app.views << 'spec/testapp/views'
|
39
|
+
config.paths.log = 'tmp/log'
|
40
|
+
config.cache_classes = true
|
41
|
+
config.whiny_nils = true
|
42
|
+
config.consider_all_requests_local = true
|
43
|
+
config.action_controller.perform_caching = false
|
44
|
+
config.action_dispatch.show_exceptions = false
|
45
|
+
config.action_controller.allow_forgery_protection = false
|
46
|
+
config.action_mailer.delivery_method = :test
|
47
|
+
config.active_support.deprecation = :stderr
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Testapp::Application.initialize!
|
52
|
+
|
53
|
+
class ClearanceCreateUsers < ActiveRecord::Migration
|
54
|
+
def self.up
|
55
|
+
create_table(:users) do |t|
|
56
|
+
t.string :email
|
57
|
+
t.string :encrypted_password, :limit => 128
|
58
|
+
t.string :salt, :limit => 128
|
59
|
+
t.string :confirmation_token, :limit => 128
|
60
|
+
t.string :remember_token, :limit => 128
|
61
|
+
t.boolean :email_confirmed, :default => false, :null => false
|
62
|
+
t.timestamps
|
63
|
+
end
|
64
|
+
|
65
|
+
add_index :users, :email
|
66
|
+
add_index :users, :remember_token
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ClearanceMailer
|
71
|
+
def self.change_password(user)
|
72
|
+
new
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.confirmation(user)
|
76
|
+
new
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.deliver_change_password(user)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.deliver_confirmation(user)
|
83
|
+
end
|
84
|
+
|
85
|
+
def deliver
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Clearance.configure do |config|
|
90
|
+
end
|
91
|
+
|
92
|
+
FileUtils.rm_f(File.join(PROJECT_ROOT, 'tmp', 'test.sqlite3'))
|
93
|
+
ClearanceCreateUsers.suppress_messages { ClearanceCreateUsers.migrate(:up) }
|
94
|
+
CreateGimmeGimmeTables.suppress_messages { CreateGimmeGimmeTables.migrate(:up) }
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CreditCard, 'by default' do
|
4
|
+
it { should belong_to(:user) }
|
5
|
+
it { should validate_presence_of(:expiration_month) }
|
6
|
+
it { should validate_presence_of(:expiration_year) }
|
7
|
+
it { should validate_presence_of(:card_type) }
|
8
|
+
it { should allow_value('Visa').for(:card_type) }
|
9
|
+
it { should allow_value('Master Card').for(:card_type) }
|
10
|
+
it { should allow_value('American Express').for(:card_type) }
|
11
|
+
it { should_not allow_value('').for(:card_type) }
|
12
|
+
it { should ensure_inclusion_of(:expiration_month).in_range(1..12) }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe CreditCard, 'being created' do
|
16
|
+
subject { Factory.build(:credit_card) }
|
17
|
+
|
18
|
+
it { should validate_presence_of(:card_number) }
|
19
|
+
it { should validate_presence_of(:security_code) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe CreditCard, 'last four digits' do
|
23
|
+
subject { Factory(:credit_card, :card_number => '4111111111110000') }
|
24
|
+
its(:last_four_digits) { should == '0000' }
|
25
|
+
|
26
|
+
it "updates with the credit card" do
|
27
|
+
subject.update_attributes(:card_number => '4111111111110001')
|
28
|
+
subject.last_four_digits.should == '0001'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe CreditCard, 'successful payment profile id' do
|
33
|
+
it 'stores the correct payment profile id' do
|
34
|
+
fake_cim do |server|
|
35
|
+
credit_card = Factory(:credit_card)
|
36
|
+
credit_card.remote_customer_payment_profile_id.should ==
|
37
|
+
server.last_customer_payment_profile.customer_payment_profile_id.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "sends first and last name" do
|
42
|
+
fake_cim do |server|
|
43
|
+
credit_card = Factory(:credit_card)
|
44
|
+
profile = server.last_customer_payment_profile
|
45
|
+
profile.first_name.should == credit_card.first_name
|
46
|
+
profile.last_name.should == credit_card.last_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe CreditCard, "updating a payment profile id" do
|
52
|
+
it 'updates the credit card on the server' do
|
53
|
+
fake_cim do |server|
|
54
|
+
credit_card = Factory(:credit_card)
|
55
|
+
credit_card.update_attributes!(:expiration_month => 11, :first_name => 'Joe')
|
56
|
+
payment_profile =
|
57
|
+
server.customer_payment_profiles[credit_card.remote_customer_payment_profile_id.to_i]
|
58
|
+
payment_profile.should_not be_nil
|
59
|
+
payment_profile.expiration_month.should == 11
|
60
|
+
payment_profile.first_name.should == 'Joe'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe CreditCard, 'failing to create a payment profile' do
|
66
|
+
it 'stores the correct payment profile id' do
|
67
|
+
fake_cim do |server|
|
68
|
+
server.register_error_for('createCustomerPaymentProfileRequest', 'bad credit card')
|
69
|
+
credit_card = Factory.build(:credit_card, :card_number => 'bad')
|
70
|
+
expect { credit_card.save }.to raise_error(CreditCard::InvalidCreditCard)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
shared_examples_for 'rounds amount' do
|
76
|
+
it 'rounds to amount up when appropiate' do
|
77
|
+
fake_cim do |server|
|
78
|
+
credit_card = Factory(:credit_card)
|
79
|
+
action.call(credit_card, 0.185999)
|
80
|
+
server.last_transaction.amount.should == 0.19
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'rounds to amount down when appropiate' do
|
85
|
+
fake_cim do |server|
|
86
|
+
credit_card = Factory(:credit_card)
|
87
|
+
action.call(credit_card, 0.181999)
|
88
|
+
server.last_transaction.amount.should == 0.18
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe CreditCard, 'successful authorization' do
|
94
|
+
it 'returns the approval code' do
|
95
|
+
fake_cim do |server|
|
96
|
+
credit_card = Factory(:credit_card)
|
97
|
+
credit_card.authorize(:amount => 200).should == server.last_transaction.authorization_code.to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
it_behaves_like 'rounds amount' do
|
101
|
+
let(:action) do
|
102
|
+
lambda do |credit_card, amount|
|
103
|
+
credit_card.authorize(:amount => amount)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe CreditCard, 'failed authorization' do
|
110
|
+
subject { Factory(:credit_card) }
|
111
|
+
it 'raises AuthorizationError' do
|
112
|
+
fake_cim do |server|
|
113
|
+
server.register_error_for('createCustomerProfileTransactionRequest', 'server down or something')
|
114
|
+
lambda { subject.authorize(:amount => 200) }.
|
115
|
+
should raise_error CreditCard::AuthorizationError
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe CreditCard, 'successful charge' do
|
121
|
+
it 'creates the transaction with the provided amount' do
|
122
|
+
fake_cim do |server|
|
123
|
+
amount = 20.25
|
124
|
+
credit_card = Factory(:credit_card)
|
125
|
+
approval_code = credit_card.authorize(:amount => amount)
|
126
|
+
credit_card.charge(:amount => amount, :approval_code => approval_code)
|
127
|
+
transaction = server.transactions.last
|
128
|
+
transaction.should_not be_nil
|
129
|
+
transaction.card_number.should == credit_card.card_number
|
130
|
+
transaction.amount.should == amount
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it_behaves_like 'rounds amount' do
|
135
|
+
let(:action) do
|
136
|
+
lambda do |credit_card, amount|
|
137
|
+
approval_code = credit_card.authorize(:amount => amount)
|
138
|
+
credit_card.charge(:amount => amount, :approval_code => approval_code)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns the transaction id' do
|
144
|
+
fake_cim do |server|
|
145
|
+
amount = 20.25
|
146
|
+
credit_card = Factory(:credit_card)
|
147
|
+
approval_code = credit_card.authorize(:amount => amount)
|
148
|
+
result = credit_card.charge(:amount => amount, :approval_code => approval_code)
|
149
|
+
transaction = server.last_transaction
|
150
|
+
result.should == transaction.transaction_id.to_s
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe CreditCard, 'failed charge' do
|
156
|
+
subject { Factory(:credit_card) }
|
157
|
+
it 'raises ChargeError' do
|
158
|
+
fake_cim do |server|
|
159
|
+
approval_code = subject.authorize(:amount => 200)
|
160
|
+
server.register_error_for('createCustomerProfileTransactionRequest', 'server down or something')
|
161
|
+
lambda { subject.charge(:amount => 200, :approval_code => approval_code) }.
|
162
|
+
should raise_error CreditCard::ChargeError
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe CreditCard, "unauthorized charge" do
|
168
|
+
subject { Factory(:credit_card) }
|
169
|
+
it 'raises ChargeError' do
|
170
|
+
fake_cim do |server|
|
171
|
+
lambda { subject.charge(:amount => 200, :approval_code => 'bogus') }.
|
172
|
+
should raise_error CreditCard::ChargeError
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe CreditCard, "charge with incorrect approval code" do
|
178
|
+
subject { Factory(:credit_card) }
|
179
|
+
it 'raises ChargeError' do
|
180
|
+
fake_cim do |server|
|
181
|
+
subject.authorize(:amount => 200)
|
182
|
+
lambda { subject.charge(:amount => 200, :approval_code => 'bogus') }.
|
183
|
+
should raise_error CreditCard::ChargeError
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe CreditCard, 'gateway' do
|
189
|
+
it "delegates to the CimGateway instance" do
|
190
|
+
CreditCard.gateway.should == GimmeGimme::CimGateway.instance.gateway
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe CreditCard, 'destroy' do
|
195
|
+
it 'should delete a customer payment profile when destroyed' do
|
196
|
+
fake_cim do |server|
|
197
|
+
credit_card = Factory(:credit_card)
|
198
|
+
credit_card.destroy
|
199
|
+
server.customer_payment_profiles.should be_empty
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe CreditCard, 'store_card?' do
|
205
|
+
subject { Factory(:credit_card) }
|
206
|
+
|
207
|
+
it 'returns false when store_card is not set' do
|
208
|
+
subject.store_card?.should be_false
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'returns false when store_card is false' do
|
212
|
+
subject.store_card?.should be_false
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'returns true when store_card is true' do
|
216
|
+
subject.store_card = true
|
217
|
+
subject.store_card?.should be_true
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe User do
|
4
|
+
it "adds description and email to cim" do
|
5
|
+
response = stub('response', :authorization => 'abc123')
|
6
|
+
User.gateway.stubs(:create_customer_profile => response)
|
7
|
+
|
8
|
+
user = Factory(:user)
|
9
|
+
|
10
|
+
User.gateway.should have_received(:create_customer_profile).
|
11
|
+
with(:profile => { :merchant_customer_id => user.customer_key,
|
12
|
+
:description => user.description,
|
13
|
+
:email => user.email })
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defaults the description to email" do
|
17
|
+
user = Factory.build(:user)
|
18
|
+
user.description.should == user.email
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
ENV["RAILS_ENV"] ||= 'test'
|
3
|
+
|
4
|
+
if File.exist?("config/environment.rb")
|
5
|
+
require "config/environment"
|
6
|
+
else
|
7
|
+
require File.expand_path("../environment", __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rspec/rails'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
14
|
+
# in spec/support/ and its subdirectories.
|
15
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.mock_with :mocha
|
19
|
+
|
20
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
21
|
+
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
22
|
+
|
23
|
+
# instead of true.
|
24
|
+
config.use_transactional_fixtures = true
|
25
|
+
config.backtrace_clean_patterns << %r{gems/}
|
26
|
+
config.include GimmeGimme::FakeCimHelpers
|
27
|
+
end
|
28
|
+
|
29
|
+
server = GimmeGimme::FakeCimServer.new
|
30
|
+
GimmeGimme::CimGateway.instance.gateway = GimmeGimme::FakeCimGateway.new(:server => server)
|