atmos-braintree_transparent_redirect_slice 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/LICENSE +20 -0
  2. data/README +41 -0
  3. data/Rakefile +65 -0
  4. data/TODO +15 -0
  5. data/app/controllers/application.rb +5 -0
  6. data/app/controllers/credit_cards.rb +54 -0
  7. data/app/controllers/main.rb +7 -0
  8. data/app/controllers/payments.rb +17 -0
  9. data/app/helpers/application_helper.rb +63 -0
  10. data/app/helpers/credit_cards_helper.rb +15 -0
  11. data/app/models/braintree/gateway_request.rb +55 -0
  12. data/app/models/braintree/gateway_response.rb +101 -0
  13. data/app/models/braintree/query.rb +46 -0
  14. data/app/models/braintree/transaction_info.rb +20 -0
  15. data/app/models/credit_card.rb +18 -0
  16. data/app/models/credit_card_info.rb +31 -0
  17. data/app/models/credit_card_invoice.rb +15 -0
  18. data/app/views/braintree_transparent_redirect_slice/credit_cards/_form.html.haml +27 -0
  19. data/app/views/braintree_transparent_redirect_slice/credit_cards/_gateway_request.html.haml +6 -0
  20. data/app/views/braintree_transparent_redirect_slice/credit_cards/destroy.html.haml +8 -0
  21. data/app/views/braintree_transparent_redirect_slice/credit_cards/edit.html.haml +8 -0
  22. data/app/views/braintree_transparent_redirect_slice/credit_cards/index.html.haml +26 -0
  23. data/app/views/braintree_transparent_redirect_slice/credit_cards/new.html.haml +9 -0
  24. data/app/views/braintree_transparent_redirect_slice/credit_cards/show.html.haml +33 -0
  25. data/app/views/braintree_transparent_redirect_slice/payments/index.html.haml +2 -0
  26. data/app/views/braintree_transparent_redirect_slice/payments/new.html.haml +21 -0
  27. data/app/views/layout/braintree_transparent_redirect_slice.html.haml +26 -0
  28. data/app/views/main/index.html.haml +1 -0
  29. data/lib/braintree_transparent_redirect_slice.rb +103 -0
  30. data/lib/braintree_transparent_redirect_slice/merbtasks.rb +103 -0
  31. data/lib/braintree_transparent_redirect_slice/slicetasks.rb +20 -0
  32. data/lib/braintree_transparent_redirect_slice/spectasks.rb +53 -0
  33. data/lib/braintree_transparent_redirect_slice/version.rb +3 -0
  34. data/public/javascripts/master.js +6 -0
  35. data/public/stylesheets/master.css +153 -0
  36. data/spec/fixtures/user.rb +24 -0
  37. data/spec/models/credit_card_info_spec.rb +30 -0
  38. data/spec/requests/credit_cards/adding_a_card_spec.rb +110 -0
  39. data/spec/requests/credit_cards/deleting_a_card_spec.rb +19 -0
  40. data/spec/requests/credit_cards/updating_a_card_spec.rb +56 -0
  41. data/spec/requests/main_spec.rb +14 -0
  42. data/spec/requests/payments/issuing_a_transaction_spec.rb +49 -0
  43. data/spec/spec_helper.rb +127 -0
  44. data/spec/spec_helpers/braintree/api_helper.rb +14 -0
  45. data/spec/spec_helpers/edit_form_helper.rb +34 -0
  46. data/stubs/app/controllers/application.rb +2 -0
  47. data/stubs/app/controllers/main.rb +2 -0
  48. metadata +212 -0
@@ -0,0 +1,20 @@
1
+ namespace :slices do
2
+ namespace :braintree_transparent_redirect_slice do
3
+
4
+ # add your own braintree_transparent_redirect_slice tasks here
5
+
6
+ # # Uncomment the following lines and edit the pre defined tasks
7
+ #
8
+ # # implement this to test for structural/code dependencies
9
+ # # like certain directories or availability of other files
10
+ # desc "Test for any dependencies"
11
+ # task :preflight do
12
+ # end
13
+ #
14
+ # # implement this to perform any database related setup steps
15
+ # desc "Migrate the database"
16
+ # task :migrate do
17
+ # end
18
+
19
+ end
20
+ end
@@ -0,0 +1,53 @@
1
+ namespace :slices do
2
+ namespace :braintree_transparent_redirect_slice do
3
+
4
+ desc "Run slice specs within the host application context"
5
+ task :spec => [ "spec:explain", "spec:default" ]
6
+
7
+ namespace :spec do
8
+
9
+ slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
+
11
+ task :explain do
12
+ puts "\nNote: By running BraintreeTransparentRedirectSlice specs inside the application context any\n" +
13
+ "overrides could break existing specs. This isn't always a problem,\n" +
14
+ "especially in the case of views. Use these spec tasks to check how\n" +
15
+ "well your application conforms to the original slice implementation."
16
+ end
17
+
18
+ Spec::Rake::SpecTask.new('default') do |t|
19
+ t.spec_opts = ["--format", "specdoc", "--colour"]
20
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
21
+ end
22
+
23
+ desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
24
+ Spec::Rake::SpecTask.new('model') do |t|
25
+ t.spec_opts = ["--format", "specdoc", "--colour"]
26
+ if(ENV['MODEL'])
27
+ t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
28
+ else
29
+ t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
30
+ end
31
+ end
32
+
33
+ desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest"
34
+ Spec::Rake::SpecTask.new('request') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--colour"]
36
+ if(ENV['REQUEST'])
37
+ t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort
38
+ else
39
+ t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort
40
+ end
41
+ end
42
+
43
+ desc "Run all specs and output the result in html"
44
+ Spec::Rake::SpecTask.new('html') do |t|
45
+ t.spec_opts = ["--format", "html"]
46
+ t.libs = ['lib', 'server/lib' ]
47
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module BraintreeTransparentRedirectSlice
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ // Common JavaScript code across your application goes here.
2
+ $(function() {
3
+ jQuery(document).ready(function($) {
4
+ $('#add_card_form').validate();
5
+ });
6
+ });
@@ -0,0 +1,153 @@
1
+ html, body {
2
+ font-family: Arial, Verdana, sans-serif;
3
+ font-size: 12px;
4
+ background-color: #fff;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+ * {
9
+ margin: 0px;
10
+ padding: 0px;
11
+ text-decoration: none;
12
+ }
13
+ html {
14
+ height: 100%;
15
+ margin-bottom: 1px;
16
+ }
17
+ #container {
18
+ width: 80%;
19
+ text-align: left;
20
+ background-color: #fff;
21
+ margin-right: auto;
22
+ margin-left: auto;
23
+ }
24
+ #header-container {
25
+ width: 100%;
26
+ padding-top: 15px;
27
+ }
28
+ #header-container h1, #header-container h2 {
29
+ margin-left: 6px;
30
+ margin-bottom: 6px;
31
+ }
32
+ .spacer {
33
+ width: 100%;
34
+ height: 15px;
35
+ }
36
+ hr {
37
+ border: 0px;
38
+ color: #ccc;
39
+ background-color: #cdcdcd;
40
+ height: 1px;
41
+ width: 100%;
42
+ text-align: left;
43
+ }
44
+ h1 {
45
+ font-size: 28px;
46
+ color: #c55;
47
+ background-color: #fff;
48
+ font-family: Arial, Verdana, sans-serif;
49
+ font-weight: 300;
50
+ }
51
+ h2 {
52
+ font-size: 15px;
53
+ color: #999;
54
+ font-family: Arial, Verdana, sans-serif;
55
+ font-weight: 300;
56
+ background-color: #fff;
57
+ }
58
+ h3 {
59
+ color: #4d9b12;
60
+ font-size: 15px;
61
+ text-align: left;
62
+ font-weight: 300;
63
+ padding: 5px;
64
+ margin-top: 5px;
65
+ }
66
+
67
+ #left-container {
68
+ float: left;
69
+ width: 250px;
70
+ background-color: #FFFFFF;
71
+ color: black;
72
+ }
73
+
74
+ #left-container ul {
75
+ list-style-type: none;
76
+ font-size: 1.4em;
77
+ padding-left: 15px;
78
+ }
79
+
80
+ #left-container h3 {
81
+ color: #c55;
82
+ }
83
+
84
+ #main-container {
85
+ margin: 5px 5px 5px 260px;
86
+ padding: 15px;
87
+ border-left: 1px solid silver;
88
+ min-height: 400px;
89
+ }
90
+ p {
91
+ color: #000;
92
+ background-color: #fff;
93
+ line-height: 20px;
94
+ padding: 5px;
95
+ }
96
+ a {
97
+ color: #4d9b12;
98
+ background-color: #fff;
99
+ text-decoration: none;
100
+ }
101
+ a:hover {
102
+ color: #4d9b12;
103
+ background-color: #fff;
104
+ text-decoration: underline;
105
+ }
106
+ #footer-container {
107
+ clear: both;
108
+ font-size: 12px;
109
+ font-family: Verdana, Arial, sans-serif;
110
+ }
111
+ .right {
112
+ float: right;
113
+ font-size: 100%;
114
+ margin-top: 5px;
115
+ color: #999;
116
+ background-color: #fff;
117
+ }
118
+ .left {
119
+ float: left;
120
+ font-size: 100%;
121
+ margin-top: 5px;
122
+ color: #999;
123
+ background-color: #fff;
124
+ }
125
+ #main-container ul {
126
+ margin-left: 3.0em;
127
+ }
128
+ fieldset {
129
+ padding: 7px;
130
+ }
131
+ input {
132
+ display: block;
133
+ margin: 2px;
134
+ }
135
+ table {
136
+ margin: 3px;
137
+ border: 1px solid #000;
138
+ }
139
+ table tr {
140
+ border: none;
141
+ padding: 0;
142
+ margin: 0;
143
+ }
144
+ table th {
145
+ border: none;
146
+ padding: 0px 7px;
147
+ margin: 0;
148
+ }
149
+
150
+ table td {
151
+ padding: 1px 7px;
152
+ border-top: 1px solid #000;
153
+ }
@@ -0,0 +1,24 @@
1
+ class User
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :login, String
6
+ property :email, String
7
+
8
+ has n, :credit_cards
9
+ end
10
+ require 'merb-auth-more/mixins/salted_user'
11
+ require 'merb-auth-slice-password'
12
+ Merb::Authentication.user_class = User
13
+ Merb::Authentication.user_class.class_eval{ include Merb::Authentication::Mixins::SaltedUser }
14
+ Merb::Authentication.activate!(:default_password_form)
15
+
16
+ class Merb::Authentication
17
+ def fetch_user(session_user_id)
18
+ Merb::Authentication.user_class.get(session_user_id)
19
+ end
20
+
21
+ def store_user(user)
22
+ user.nil? ? user : user.id
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
3
+ describe "CreditCard" do
4
+ before(:all) do
5
+ mount_slice
6
+ end
7
+ describe "#info", :given => 'a user with a credit card in the vault' do
8
+ it "should return all the required information" do
9
+ cc = CreditCard.get(1)
10
+ cc.info.first_name.should == "Quentin"
11
+ cc.info.last_name.should == "Blake"
12
+ cc.info.email.should == "quentin@example.org"
13
+ cc.info.address_1.should == "187 Drive By Blvd"
14
+ cc.info.city.should == "Compton"
15
+ cc.info.state.should == "CA"
16
+ cc.info.postal_code.should == "90220"
17
+ cc.info.country.should == "US"
18
+ cc.info.cc_number.should == "4xxxxxxxxxxx1111"
19
+ cc.info.cc_exp.should == "1010"
20
+ end
21
+ end
22
+ describe "#info with bad input" do
23
+ it "should return empty strings if there's nil input on initialize" do
24
+ CreditCardInfo.new.address_1.should == ''
25
+ end
26
+ it "should return empty strings if there's an empty string on initialize" do
27
+ CreditCardInfo.new('').address_1.should == ''
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
2
+
3
+ describe "" do
4
+ before(:each) do
5
+ mount_slice
6
+ end
7
+ describe "visiting /billing/credit_cards/new", :given => 'an authenticated user' do
8
+ it "should display a form with the necessary input to create a vault entry" do
9
+ response = request("/billing/credit_cards/new")
10
+ response.should be_successful
11
+ response.should have_selector("form[action='https://secure.braintreepaymentgateway.com/api/transact.php'][method='post']")
12
+ response.should have_selector("form input#firstname[value='']")
13
+ response.should have_selector("form input#lastname[value='']")
14
+ response.should have_selector("form input#email[value='']")
15
+ response.should have_selector("form input#address1[value='']")
16
+ response.should have_selector("form input#city[value='']")
17
+ response.should have_selector("form input#state[value='']")
18
+ response.should have_selector("form input#country[value='']")
19
+ response.should have_selector("form input#ccnumber[value='']")
20
+ response.should have_selector("form input#ccexp[value='']")
21
+ response.should have_selector("form input#cvv[value=''][type='text']")
22
+
23
+ response.should have_selector("form input#type[value='sale'][type='hidden']")
24
+ response.should have_selector("form input#amount[value='10.00'][type='hidden']")
25
+ response.should have_selector("form input#orderid[value=''][type='hidden']")
26
+ response.should have_selector("form input#hash[type='hidden']")
27
+ response.should have_selector("form input#time[type='hidden']")
28
+ response.should have_selector("form input#customer_vault[type='hidden'][value='add_customer']")
29
+ response.should have_selector("form input#redirect[type='hidden'][value='http://example.org/billing/credit_cards/new_response']")
30
+ end
31
+ end
32
+
33
+ describe "submitting the form at /billing/credit_cards/new", :given => 'an authenticated user' do
34
+ describe "and having it succeed" do
35
+ it "should display basic info about the card stored in the vault" do
36
+ params = quentin_form_info.merge({'type'=>'sale','payment' => 'creditcard'})
37
+ api_response = Braintree::Spec::ApiRequest.new('10.00', nil, params)
38
+
39
+ response = request("/billing/credit_cards/new_response", :params => api_response.params)
40
+ response.should redirect_to('/billing/credit_cards')
41
+
42
+ response = request(response.headers['Location'])
43
+ response.should be_successful
44
+ response.should have_selector("div#braintree-message:contains('Successfully stored your card info securely.')")
45
+ response.should have_selector("table#braintree-card-info tbody td")
46
+ end
47
+ end
48
+
49
+ describe "and having it declined" do
50
+ it "should display the signup form again, pre-populated with the info from the failed transaction" do
51
+ params = quentin_form_info.merge({'type'=>'sale','payment' => 'creditcard'})
52
+ api_response = Braintree::Spec::ApiRequest.new('0.99', nil, params)
53
+
54
+ response = request("/billing/credit_cards/new_response", :params => api_response.params)
55
+ response.should redirect_to("/billing/credit_cards/new")
56
+
57
+ response = request(response.headers['Location'])
58
+ response.should be_successful
59
+ response.should have_selector("div#braintree-message:contains('DECLINE')")
60
+ response.should have_selector("form[action='https://secure.braintreepaymentgateway.com/api/transact.php'][method='post']")
61
+ response.should have_selector("form input#firstname[value='Quentin']")
62
+ response.should have_selector("form input#lastname[value='Blake']")
63
+ response.should have_selector("form input#email[value='quentin@example.org']")
64
+ response.should have_selector("form input#address1[value='187 Drive By Blvd']")
65
+ response.should have_selector("form input#city[value='Compton']")
66
+ response.should have_selector("form input#state[value='CA']")
67
+ response.should have_selector("form input#zip[value='90220']")
68
+ response.should have_selector("form input#country[value='US']")
69
+ response.should have_selector("form input#ccnumber[value='']")
70
+ response.should have_selector("form input#ccexp[value='1010']")
71
+ response.should have_selector("form input#cvv[value=''][type='text']")
72
+
73
+ response.should have_selector("form input#orderid[value=''][type='hidden']")
74
+ response.should have_selector("form input#hash[type='hidden']")
75
+ response.should have_selector("form input#time[type='hidden']")
76
+ response.should have_selector("form input#customer_vault[type='hidden'][value='add_customer']")
77
+ end
78
+ end
79
+ describe "and having it declined for bad cvv" do
80
+ it "should display the signup form again, pre-populated with the info from the failed transaction" do
81
+ params = quentin_form_info.merge({'type'=>'sale','payment' => 'creditcard', 'cvv' => '911'})
82
+ api_response = Braintree::Spec::ApiRequest.new('10.00', nil, params)
83
+
84
+ response = request("/billing/credit_cards/new_response", :params => api_response.params)
85
+ response.should redirect_to("/billing/credit_cards/new")
86
+
87
+ response = request(response.headers['Location'])
88
+ response.should be_successful
89
+ response.should have_selector("div#braintree-message:contains('CVV2/CVC2 No Match')")
90
+ response.should have_selector("form[action='https://secure.braintreepaymentgateway.com/api/transact.php'][method='post']")
91
+ response.should have_selector("form input#firstname[value='Quentin']")
92
+ response.should have_selector("form input#lastname[value='Blake']")
93
+ response.should have_selector("form input#email[value='quentin@example.org']")
94
+ response.should have_selector("form input#address1[value='187 Drive By Blvd']")
95
+ response.should have_selector("form input#city[value='Compton']")
96
+ response.should have_selector("form input#state[value='CA']")
97
+ response.should have_selector("form input#zip[value='90220']")
98
+ response.should have_selector("form input#country[value='US']")
99
+ response.should have_selector("form input#ccnumber[value='']")
100
+ response.should have_selector("form input#ccexp[value='1010']")
101
+ response.should have_selector("form input#cvv[value=''][type='text']")
102
+
103
+ response.should have_selector("form input#orderid[value=''][type='hidden']")
104
+ response.should have_selector("form input#hash[type='hidden']")
105
+ response.should have_selector("form input#time[type='hidden']")
106
+ response.should have_selector("form input#customer_vault[type='hidden'][value='add_customer']")
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
2
+
3
+ describe "" do
4
+ before(:each) do
5
+ mount_slice
6
+ end
7
+ describe "visiting /billing/credit_cards/1/delete", :given => "a user with a credit card in the vault" do
8
+ before(:each) do
9
+ @token = User.first.credit_cards.first.token
10
+ end
11
+ it "should display a form to submit the delete request to the vault" do
12
+ response = request("/billing/credit_cards/1", :method => 'DELETE')
13
+ response.should be_successful
14
+ response.should have_selector("form[action='https://secure.braintreepaymentgateway.com/api/transact.php'][method='post']")
15
+ response.should have_selector("form input#customer_vault_id[value='#{@token}']")
16
+ response.should have_selector("form input#customer_vault[value='delete_customer']")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,56 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper.rb')
2
+
3
+ describe "" do
4
+ before(:each) do
5
+ mount_slice
6
+ end
7
+ describe "visiting /billing/credit_cards/1/edit", :given => 'a user with a credit card in the vault' do
8
+ include BrainTreeEditFormHelper
9
+ it "should display a form for customer vault updating with the info pre-populated" do
10
+ response = request("/billing/credit_cards/1/edit")
11
+ response.should be_successful
12
+ response.should display_a_credit_card_edit_form_for_quentin
13
+ end
14
+ end
15
+
16
+ describe "submitting the form at /billing/credit_cards/1/edit", :given => 'a user with a credit card in the vault' do
17
+ before(:each) do
18
+ @token = User.first.credit_cards.first.token
19
+ end
20
+ include BrainTreeEditFormHelper
21
+ describe "and a successful response" do
22
+ it "tells the user that updating succeeded" do
23
+ query_params = {'ccexp' => '1011', 'customer_vault' => 'update_customer',
24
+ 'customer_vault_id' => User.first.credit_cards.first.token,
25
+ 'redirect' => 'http://example.org/billing/credit_cards/1/edit_response' }
26
+
27
+ api_response = Braintree::Spec::ApiRequest.new('', @token, query_params)
28
+
29
+ response = request("/billing/credit_cards/1/edit_response", :params => api_response.params)
30
+ response.should redirect_to('/billing/credit_cards')
31
+
32
+ response = request(response.headers['Location'])
33
+ response.should be_successful
34
+ response.should have_selector("div#braintree-message:contains('Successfully updated your info in the vault.')")
35
+ response.should have_selector("table#braintree-card-info tbody td")
36
+ end
37
+ end
38
+ describe "and a missing ccexp field" do
39
+ it "tells the user that updating failed" do
40
+ query_params = {'ccexp' => '', 'customer_vault' => 'update_customer',
41
+ 'customer_vault_id' => User.first.credit_cards.first.token,
42
+ 'redirect' => 'http://example.org/billing/credit_cards/1/edit_response' }
43
+
44
+ api_response = Braintree::Spec::ApiRequest.new('', @token, query_params)
45
+
46
+ response = request("/billing/credit_cards/1/edit_response", :params => api_response.params)
47
+ response.should redirect_to('/billing/credit_cards/1/edit')
48
+
49
+ response = request(response.headers['Location'])
50
+ response.should be_successful
51
+ response.should have_selector("div#braintree-message:contains('Field required: ccexp REFID:')")
52
+ response.should display_a_credit_card_edit_form_for_quentin
53
+ end
54
+ end
55
+ end
56
+ end