codehog-google-checkout 1

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 (42) hide show
  1. data/History.txt +25 -0
  2. data/Isolate +13 -0
  3. data/MIT-LICENSE.txt +23 -0
  4. data/Manifest.txt +42 -0
  5. data/README.txt +148 -0
  6. data/Rakefile +35 -0
  7. data/examples/google_notifications_controller.rb +159 -0
  8. data/lib/duck_punches/hpricot.rb +24 -0
  9. data/lib/google-checkout.rb +61 -0
  10. data/lib/google-checkout/cart.rb +357 -0
  11. data/lib/google-checkout/command.rb +191 -0
  12. data/lib/google-checkout/notification.rb +226 -0
  13. data/spec/fixtures/google/checkout-shopping-cart.xml +22 -0
  14. data/spec/fixtures/google/commands/add-merchant-order-number.xml +5 -0
  15. data/spec/fixtures/google/commands/add-tracking-data.xml +8 -0
  16. data/spec/fixtures/google/commands/archive-order.xml +3 -0
  17. data/spec/fixtures/google/commands/authorize-order.xml +2 -0
  18. data/spec/fixtures/google/commands/cancel-order.xml +5 -0
  19. data/spec/fixtures/google/commands/charge-order.xml +4 -0
  20. data/spec/fixtures/google/commands/deliver-order.xml +9 -0
  21. data/spec/fixtures/google/commands/process-order.xml +2 -0
  22. data/spec/fixtures/google/commands/refund-order.xml +6 -0
  23. data/spec/fixtures/google/commands/send-buyer-message.xml +7 -0
  24. data/spec/fixtures/google/commands/unarchive-order.xml +2 -0
  25. data/spec/fixtures/google/notifications/authorization-amount-notification.xml +10 -0
  26. data/spec/fixtures/google/notifications/charge-amount-notification.xml +8 -0
  27. data/spec/fixtures/google/notifications/chargeback-amount-notification.xml +8 -0
  28. data/spec/fixtures/google/notifications/new-order-notification.xml +85 -0
  29. data/spec/fixtures/google/notifications/order-state-change-notification.xml +11 -0
  30. data/spec/fixtures/google/notifications/refund-amount-notification.xml +8 -0
  31. data/spec/fixtures/google/notifications/risk-information-notification.xml +23 -0
  32. data/spec/fixtures/google/responses/checkout-redirect.xml +5 -0
  33. data/spec/fixtures/google/responses/error.xml +5 -0
  34. data/spec/fixtures/google/responses/request-received.xml +3 -0
  35. data/spec/google-checkout/cart_spec.rb +165 -0
  36. data/spec/google-checkout/command_spec.rb +131 -0
  37. data/spec/google-checkout/notification_spec.rb +181 -0
  38. data/spec/google-checkout/response_spec.rb +49 -0
  39. data/spec/google-checkout_spec.rb +15 -0
  40. data/spec/spec_helper.rb +47 -0
  41. data/support/cacert.pem +7815 -0
  42. metadata +203 -0
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <refund-amount-notification xmlns="http://checkout.google.com/schema/2"
3
+ serial-number="bea6bc1b-e1e2-44fe-80ff-0180e33a2614">
4
+ <google-order-number>841171949013218</google-order-number>
5
+ <latest-refund-amount currency="USD">226.06</latest-refund-amount>
6
+ <total-refund-amount currency="USD">226.06</total-refund-amount>
7
+ <timestamp>2006-03-18T20:25:31</timestamp>
8
+ </refund-amount-notification>
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <risk-information-notification xmlns="http://checkout.google.com/schema/2"
3
+ serial-number="bea6bc1b-e1e2-44fe-80ff-0180e33a2614">
4
+ <google-order-number>841171949013218</google-order-number>
5
+ <risk-information>
6
+ <eligible-for-protection>true</eligible-for-protection>
7
+ <billing-address id="BuyerBillingAddress">
8
+ <contact-name>John Smith</contact-name>
9
+ <email>johnsmith@example.com</email>
10
+ <address1>10 Example Road</address1>
11
+ <city>Sampleville</city>
12
+ <region>CA</region>
13
+ <postal-code>94141</postal-code>
14
+ <country-code>US</country-code>
15
+ </billing-address>
16
+ <avs-response>Y</avs-response>
17
+ <cvn-response>M</cvn-response>
18
+ <partial-cc-number>3719</partial-cc-number>
19
+ <ip-address>10.11.12.13</ip-address>
20
+ <buyer-account-age>6</buyer-account-age>
21
+ </risk-information>
22
+ <timestamp>2006-01-15T10:44:06</timestamp>
23
+ </risk-information-notification>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <checkout-redirect xmlns="http://checkout.google.com/schema/2"
3
+ serial-number="bea6bc1b-e1e2-44fe-80ff-0180e33a2614">
4
+ <redirect-url>https://checkout.google.com/buy?foo=bar&amp;id=8572098456</redirect-url>
5
+ </checkout-redirect>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <error xmlns="http://checkout.google.com/schema/2"
3
+ serial-number="bea6bc1b-e1e2-44fe-80ff-0180e33a2614">
4
+ <error-message>Bad username and/or password for API Access.</error-message>
5
+ </error>
@@ -0,0 +1,3 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <request-received xmlns="http://checkout.google.com/schema/2"
3
+ serial-number="bea6bc1b-e1e2-44fe-80ff-0180e33a2614"/>
@@ -0,0 +1,165 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe GoogleCheckout, "Cart (generic)" do
4
+
5
+ before(:each) do
6
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
7
+ :name => "PeepCode Screencast",
8
+ :description => "A few screencasts",
9
+ :price => 9.00
10
+ })
11
+ GoogleCheckout.use_sandbox
12
+ end
13
+
14
+ it "should generate proper live buy button_url" do
15
+ GoogleCheckout.use_production
16
+ @cart.button_url.should match(%r{https://checkout\.google\.com/buttons/buy\.gif})
17
+ end
18
+
19
+ it "should generate proper live checkout button_url" do
20
+ GoogleCheckout.use_production
21
+ @cart.button_url(:buy_or_checkout => :checkout).should match(%r{https://checkout\.google\.com/buttons/checkout\.gif})
22
+ end
23
+
24
+ it "should generate proper sandbox buy button_url" do
25
+ @cart.button_url.should match(%r{https://sandbox\.google\.com/buttons/buy\.gif})
26
+ end
27
+
28
+ it "should generate proper sandbox checkout button_url" do
29
+ @cart.button_url(:buy_or_checkout => :checkout).should match(%r{https://sandbox\.google\.com/checkout/buttons/checkout\.gif})
30
+ end
31
+
32
+ it "should generate checkout button" do
33
+ @cart.checkout_button.should match(/buy\.gif/)
34
+ end
35
+
36
+ end
37
+
38
+ describe GoogleCheckout, "Cart (currency)" do
39
+
40
+ it "should show correct currency if all items use the same currency" do
41
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
42
+ :name => "PeepCode Screencast",
43
+ :description => "A few screencasts",
44
+ :price => 9.00,
45
+ :currency => "CAD"
46
+ })
47
+ @cart.currency.should == "CAD"
48
+ @cart.add_item(
49
+ :name => "PeepCode Screencast 2",
50
+ :description => "A few more casts",
51
+ :price => 9.00,
52
+ :currency => "CAD"
53
+ )
54
+ @cart.currency.should == "CAD"
55
+ end
56
+
57
+ it "should show correct currency if all items use the same currency" do
58
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
59
+ :name => "PeepCode Screencast",
60
+ :description => "A few screencasts",
61
+ :price => 9.00,
62
+ :currency => "JPY"
63
+ })
64
+ @cart.currency.should == "JPY"
65
+ end
66
+
67
+ it "should show USD for empty cart" do
68
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key")
69
+ @cart.currency.should == "USD"
70
+ end
71
+
72
+ it "should throw an exception if currencies are mixed" do
73
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
74
+ :name => "PeepCode Screencast",
75
+ :description => "A few screencasts",
76
+ :price => 9.00,
77
+ :currency => "CAD"
78
+ })
79
+ @cart.currency.should == "CAD"
80
+ @cart.add_item(
81
+ :name => "PeepCode Screencast 2",
82
+ :description => "A few more casts",
83
+ :price => 9.00,
84
+ :currency => "USD"
85
+ )
86
+ lambda { @cart.currency }.should raise_exception(RuntimeError, "Mixing currency not allowed")
87
+ end
88
+
89
+
90
+ it "should show USD if no currency provided" do
91
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
92
+ :name => "PeepCode Screencast",
93
+ :description => "A few screencasts",
94
+ :price => 9.00
95
+ })
96
+ @cart.currency.should == "USD"
97
+ end
98
+
99
+
100
+ end
101
+
102
+ describe GoogleCheckout, "Cart Post" do
103
+
104
+ before(:each) do
105
+ @cart = GoogleCheckout::Cart.new("my_id", "my_key", {
106
+ :name => "PeepCode Screencast",
107
+ :description => "One screencast",
108
+ :price => 9.00
109
+ })
110
+ GoogleCheckout.use_sandbox
111
+ end
112
+
113
+ it "should get merchant_id" do
114
+ @cart.merchant_id.should == 'my_id'
115
+ end
116
+
117
+ it "should get merchant_key" do
118
+ @cart.merchant_key.should == 'my_key'
119
+ end
120
+
121
+ it "should post request to Google" do
122
+ # :null_object means eat all other methods and return self
123
+ net_http = mock("net_http", {:null_object => true})
124
+ Net::HTTP.should_receive(:new).and_return(net_http)
125
+
126
+ success_response = Net::HTTPSuccess.new(Net::HTTP.version_1_2, 200, "OK")
127
+ success_response.should_receive(:body).and_return(read_xml_fixture('responses/checkout-redirect'))
128
+ net_http.should_receive(:request).and_return(success_response)
129
+
130
+ response = @cart.post
131
+ response.should be_kind_of(GoogleCheckout::CheckoutRedirect)
132
+ response.serial_number.should == 'bea6bc1b-e1e2-44fe-80ff-0180e33a2614'
133
+ response.redirect_url.should == 'https://checkout.google.com/buy?foo=bar&id=8572098456'
134
+ end
135
+
136
+ it "should set merchant private data" do
137
+ @cart.merchant_private_data = {"merchant-order-number" => "1234-5678-9012"}
138
+ @cart.merchant_private_data["merchant-order-number"].should == "1234-5678-9012"
139
+ end
140
+
141
+ it "should include merchant private in the generated xml" do
142
+ @cart.merchant_private_data = {"merchant-order-number" => "1234-5678-9012"}
143
+ @cart.to_xml.should match(/<merchant-order-number>1234-5678-9012<\/merchant-order-number>/)
144
+ end
145
+
146
+ it "should not include merchant private data if none is set" do
147
+ @cart.to_xml.should_not match(/<merchant-private-data>/)
148
+ end
149
+
150
+ it "should include merchant-item-id in XML if :item_id was passed with the item" do
151
+ @cart.add_item({
152
+ :name => "Item",
153
+ :description => "Item description",
154
+ :price => "1.00",
155
+ :quantity => 1,
156
+ :item_id => "ITEM-007"
157
+ })
158
+ @cart.to_xml.should match(%r{<merchant-item-id>ITEM-007</merchant-item-id>})
159
+ end
160
+
161
+ it "should generate XML"
162
+
163
+ it "should receive error when placing false request"
164
+
165
+ end
@@ -0,0 +1,131 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe "basic command", :shared => true do
4
+
5
+ it "should include XML header" do
6
+ @order.to_xml.should match(/^<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>/)
7
+ end
8
+
9
+ end
10
+
11
+ describe GoogleCheckout, "Command class" do
12
+
13
+ it "should read SSL certs" do
14
+ GoogleCheckout::Command.x509_store.should be_kind_of(OpenSSL::X509::Store)
15
+ end
16
+
17
+ end
18
+
19
+ describe GoogleCheckout, "Command instance" do
20
+
21
+ before(:each) do
22
+ @command = GoogleCheckout::Command.new("my_id", "my_key")
23
+ end
24
+
25
+ it "should generate sandbox url" do
26
+ GoogleCheckout.use_sandbox
27
+ @command.url.should match(/sandbox/)
28
+ end
29
+
30
+ it "should generate production url" do
31
+ GoogleCheckout.use_production
32
+ @command.url.should match(/checkout\.google\.com/)
33
+ end
34
+
35
+ end
36
+
37
+ describe GoogleCheckout, "Charge Order" do
38
+
39
+ before(:each) do
40
+ @order = GoogleCheckout::ChargeOrder.new("my_id", "my_key", "1234567890")
41
+ GoogleCheckout.use_sandbox
42
+ @order.amount = 123.45
43
+ end
44
+
45
+ it_should_behave_like "basic command"
46
+
47
+ it "should retrieve order number" do
48
+ @order.google_order_number.should == '1234567890'
49
+ end
50
+
51
+ it "should get merchant_id" do
52
+ @order.merchant_id.should == 'my_id'
53
+ end
54
+
55
+ it "should get merchant_key" do
56
+ @order.merchant_key.should == 'my_key'
57
+ end
58
+
59
+ it "should generate XML" do
60
+ @order.to_xml.should match(/amount currency="USD"/)
61
+ @order.to_xml.should match(/123\.45<\/amount>/)
62
+ end
63
+
64
+ it "should post request to Google successfully" do
65
+ # :null_object means eat all other methods and return self
66
+ net_http = mock("net_http", { :null_object => true })
67
+ Net::HTTP.should_receive(:new).and_return(net_http)
68
+
69
+ success_response = Net::HTTPSuccess.new(Net::HTTP.version_1_2, 200, "OK")
70
+ success_response.should_receive(:body).and_return(read_xml_fixture('responses/request-received'))
71
+ net_http.should_receive(:request).and_return(success_response)
72
+
73
+ response = @order.post
74
+ response.should be_kind_of(GoogleCheckout::RequestReceived)
75
+ response.should_not be_error
76
+ response.serial_number.should == 'bea6bc1b-e1e2-44fe-80ff-0180e33a2614'
77
+ end
78
+
79
+ it "should post request to Google and return error" do
80
+ # :null_object means eat all other methods and return self
81
+ net_http = mock("net_http", { :null_object => true })
82
+ Net::HTTP.should_receive(:new).and_return(net_http)
83
+
84
+ # NOTE HTTP response code is irrelevant here.
85
+ error_response = Net::HTTPSuccess.new(Net::HTTP.version_1_2, 200, "OK")
86
+ error_response.should_receive(:body).and_return(read_xml_fixture('responses/error'))
87
+ net_http.should_receive(:request).and_return(error_response)
88
+
89
+ lambda { @order.post }.should raise_error(GoogleCheckout::APIError)
90
+ end
91
+
92
+ end
93
+
94
+ describe GoogleCheckout, "Checkout API Request (with Cart)" do
95
+
96
+ it "should use HTTP Basic Auth"
97
+
98
+ it "should use proper content type"
99
+
100
+ it "should use proper accept type"
101
+
102
+ it "should report success of request"
103
+
104
+ it "should report error and error message"
105
+
106
+ it "should return redirect url"
107
+
108
+ end
109
+
110
+
111
+ describe GoogleCheckout, "Send Buyer Email" do
112
+
113
+ before(:each) do
114
+ @command = GoogleCheckout::SendBuyerMessage.new("my_id", "my_key", "1234567890", "Thanks for the order!")
115
+ end
116
+
117
+ # <send-buyer-message xmlns="http://checkout.google.com/schema/2"
118
+ # google-order-number="841171949013218">
119
+ # <message>Due to high volume, your order will ship
120
+ # next week. Thank you for your patience.</message>
121
+ # <send-email>true</send-email>
122
+ # </send-buyer-message>
123
+
124
+ it "should post email to the buyer" do
125
+ xml = @command.to_xml
126
+ xml.should match(%r{google-order-number="1234567890"})
127
+ xml.should match(%r{<message>Thanks for the order!</message>})
128
+ xml.should match(%r{<send-email>true</send-email>})
129
+ end
130
+
131
+ end
@@ -0,0 +1,181 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe "basic notification", :shared => true do
4
+
5
+ it "should get serial number" do
6
+ @notification.serial_number.should == 'bea6bc1b-e1e2-44fe-80ff-0180e33a2614'
7
+ end
8
+
9
+ it "should get google order number" do
10
+ @notification.google_order_number.should == '841171949013218'
11
+ end
12
+
13
+ it "should generate acknowledgment XML" do
14
+ @notification.acknowledgment_xml.should match(/notification-acknowledgment/)
15
+ end
16
+
17
+ end
18
+
19
+ describe GoogleCheckout, "New Order Notification" do
20
+
21
+ before(:each) do
22
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/new-order-notification'))
23
+ end
24
+
25
+ it "should identify type of notification" do
26
+ @notification.should be_kind_of(GoogleCheckout::NewOrderNotification)
27
+ end
28
+
29
+ it_should_behave_like "basic notification"
30
+
31
+ it "should find fulfillment order state" do
32
+ @notification.fulfillment_order_state.should == 'NEW'
33
+ end
34
+
35
+ it "should find financial order state" do
36
+ @notification.financial_order_state.should == 'REVIEWING'
37
+ end
38
+
39
+ it "should use financial state shortcut" do
40
+ @notification.state.should == "REVIEWING"
41
+ end
42
+
43
+ it "should create Money object from order total" do
44
+ @notification.order_total.should be_kind_of(Money)
45
+ @notification.order_total.cents.should == 19098
46
+ @notification.order_total.currency.iso_code.should == 'USD'
47
+ end
48
+
49
+ it "should throw error when accessing non-existent value" do
50
+ lambda { @notification.there_is_no_field_with_this_name }.should raise_error(NoMethodError)
51
+ end
52
+
53
+ it "should find sub-keys of merchant-private-data as if they were at the root" do
54
+ @notification.peepcode_order_number.should == '1234-5678-9012'
55
+ end
56
+
57
+ it "should find total tax" do
58
+ @notification.total_tax.should be_kind_of(Money)
59
+ @notification.total_tax.cents.should == 0
60
+ end
61
+
62
+ it "should find email marketing allowed" do
63
+ @notification.email_allowed.should be_false
64
+ end
65
+
66
+ it "should get email or buyer-shipping-address/email or buyer-billing-address/email"
67
+
68
+ end
69
+
70
+
71
+ describe GoogleCheckout, "Order State Change Notification" do
72
+
73
+ before(:each) do
74
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/order-state-change-notification'))
75
+ end
76
+
77
+ it_should_behave_like "basic notification"
78
+
79
+ it "should identify type of notification" do
80
+ @notification.should be_kind_of(GoogleCheckout::OrderStateChangeNotification)
81
+ end
82
+
83
+ it "should find new financial state" do
84
+ @notification.new_financial_order_state.should == 'CHARGING'
85
+ end
86
+
87
+ it "should find new fulfillment state" do
88
+ @notification.new_fulfillment_order_state.should == 'NEW'
89
+ end
90
+
91
+ it "should use financial state shortcut" do
92
+ @notification.state.should == 'CHARGING'
93
+ end
94
+
95
+ end
96
+
97
+ describe GoogleCheckout, "Risk Information Notification" do
98
+
99
+ before(:each) do
100
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/risk-information-notification'))
101
+ end
102
+
103
+ it "should identify type of notification" do
104
+ @notification.should be_kind_of(GoogleCheckout::RiskInformationNotification)
105
+ end
106
+
107
+ it_should_behave_like "basic notification"
108
+
109
+ end
110
+
111
+ describe GoogleCheckout, "Charge Amount Notification" do
112
+
113
+ before(:each) do
114
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/charge-amount-notification'))
115
+ end
116
+
117
+ it_should_behave_like "basic notification"
118
+
119
+ it "should identify type of notification" do
120
+ @notification.should be_kind_of(GoogleCheckout::ChargeAmountNotification)
121
+ end
122
+
123
+ it "should get latest charge amount" do
124
+ @notification.latest_charge_amount.should be_kind_of(Money)
125
+ end
126
+
127
+ it "should get total charge amount" do
128
+ @notification.total_charge_amount.should be_kind_of(Money)
129
+ @notification.total_charge_amount.cents.should == 22606
130
+ end
131
+
132
+ end
133
+
134
+ describe GoogleCheckout, "Authorization Amount Notification" do
135
+
136
+ before(:each) do
137
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/authorization-amount-notification'))
138
+ end
139
+
140
+ it_should_behave_like "basic notification"
141
+
142
+ it "should identify type of notification" do
143
+ @notification.should be_kind_of(GoogleCheckout::AuthorizationAmountNotification)
144
+ end
145
+
146
+ end
147
+
148
+ describe GoogleCheckout, "Chargeback Amount Notification" do
149
+
150
+ before(:each) do
151
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/chargeback-amount-notification'))
152
+ end
153
+
154
+ it_should_behave_like "basic notification"
155
+
156
+ it "identifies type of notification" do
157
+ @notification.should be_kind_of(GoogleCheckout::ChargebackAmountNotification)
158
+ end
159
+
160
+ it "parses chargeback amounts as money objects" do
161
+ @notification.latest_chargeback_amount.cents.should == 22606
162
+ @notification.total_chargeback_amount.cents.should == 22606
163
+ @notification.latest_chargeback_amount.currency.iso_code.should == "USD"
164
+ end
165
+
166
+ end
167
+
168
+ describe GoogleCheckout, "Refund Amount Notification" do
169
+
170
+ before(:each) do
171
+ @notification = GoogleCheckout::Notification.parse(read_xml_fixture('notifications/refund-amount-notification'))
172
+ end
173
+
174
+ it_should_behave_like "basic notification"
175
+
176
+ it "should identify type of notification" do
177
+ @notification.should be_kind_of(GoogleCheckout::RefundAmountNotification)
178
+ end
179
+
180
+ end
181
+