topfunky-google-checkout 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/History.txt +14 -0
  2. data/MIT-LICENSE.txt +23 -0
  3. data/Manifest.txt +40 -0
  4. data/README.txt +139 -0
  5. data/Rakefile +21 -0
  6. data/examples/google_notifications_controller.rb +159 -0
  7. data/lib/duck_punches/hpricot.rb +24 -0
  8. data/lib/google-checkout.rb +61 -0
  9. data/lib/google-checkout/cart.rb +349 -0
  10. data/lib/google-checkout/command.rb +191 -0
  11. data/lib/google-checkout/notification.rb +212 -0
  12. data/spec/fixtures/google/checkout-shopping-cart.xml +22 -0
  13. data/spec/fixtures/google/commands/add-merchant-order-number.xml +5 -0
  14. data/spec/fixtures/google/commands/add-tracking-data.xml +8 -0
  15. data/spec/fixtures/google/commands/archive-order.xml +3 -0
  16. data/spec/fixtures/google/commands/authorize-order.xml +2 -0
  17. data/spec/fixtures/google/commands/cancel-order.xml +5 -0
  18. data/spec/fixtures/google/commands/charge-order.xml +4 -0
  19. data/spec/fixtures/google/commands/deliver-order.xml +9 -0
  20. data/spec/fixtures/google/commands/process-order.xml +2 -0
  21. data/spec/fixtures/google/commands/refund-order.xml +6 -0
  22. data/spec/fixtures/google/commands/send-buyer-message.xml +7 -0
  23. data/spec/fixtures/google/commands/unarchive-order.xml +2 -0
  24. data/spec/fixtures/google/notifications/authorization-amount-notification.xml +10 -0
  25. data/spec/fixtures/google/notifications/charge-amount-notification.xml +8 -0
  26. data/spec/fixtures/google/notifications/chargeback-amount-notification.xml +8 -0
  27. data/spec/fixtures/google/notifications/new-order-notification.xml +85 -0
  28. data/spec/fixtures/google/notifications/order-state-change-notification.xml +11 -0
  29. data/spec/fixtures/google/notifications/refund-amount-notification.xml +8 -0
  30. data/spec/fixtures/google/notifications/risk-information-notification.xml +23 -0
  31. data/spec/fixtures/google/responses/checkout-redirect.xml +5 -0
  32. data/spec/fixtures/google/responses/error.xml +5 -0
  33. data/spec/fixtures/google/responses/request-received.xml +3 -0
  34. data/spec/google-checkout/cart_spec.rb +101 -0
  35. data/spec/google-checkout/command_spec.rb +131 -0
  36. data/spec/google-checkout/notification_spec.rb +175 -0
  37. data/spec/google-checkout/response_spec.rb +49 -0
  38. data/spec/google-checkout_spec.rb +15 -0
  39. data/spec/spec_helper.rb +47 -0
  40. data/support/cacert.pem +7815 -0
  41. metadata +114 -0
data/History.txt ADDED
@@ -0,0 +1,14 @@
1
+ == 0.2.0 / 2007-08-14
2
+
3
+ * Significant rewrite!
4
+ * Added rSpec specifications.
5
+ * Added level 2 integration for sending and
6
+ and parsing notifications from Google.
7
+ * Shipping temporarily hard-coded to
8
+ "Digital Download"
9
+ * Added merchant-private-data for sending
10
+ one's own tracking number to Google.
11
+ * Defaults to live system but you can call
12
+ GoogleCheckout.use_sandbox to use the
13
+ sandbox instead.
14
+ * Fixtures for Google commands and notifications.
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2006-2008 Peter Elmore (pete.elmore at gmail.com) and Topfunky Corporation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/Manifest.txt ADDED
@@ -0,0 +1,40 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/duck_punches/hpricot.rb
6
+ lib/google-checkout.rb
7
+ lib/google-checkout/cart.rb
8
+ lib/google-checkout/command.rb
9
+ lib/google-checkout/notification.rb
10
+ spec/fixtures/google/checkout-shopping-cart.xml
11
+ spec/fixtures/google/commands/add-merchant-order-number.xml
12
+ spec/fixtures/google/commands/add-tracking-data.xml
13
+ spec/fixtures/google/commands/archive-order.xml
14
+ spec/fixtures/google/commands/authorize-order.xml
15
+ spec/fixtures/google/commands/cancel-order.xml
16
+ spec/fixtures/google/commands/charge-order.xml
17
+ spec/fixtures/google/commands/deliver-order.xml
18
+ spec/fixtures/google/commands/process-order.xml
19
+ spec/fixtures/google/commands/refund-order.xml
20
+ spec/fixtures/google/commands/send-buyer-message.xml
21
+ spec/fixtures/google/commands/unarchive-order.xml
22
+ spec/fixtures/google/notifications/authorization-amount-notification.xml
23
+ spec/fixtures/google/notifications/charge-amount-notification.xml
24
+ spec/fixtures/google/notifications/chargeback-amount-notification.xml
25
+ spec/fixtures/google/notifications/new-order-notification.xml
26
+ spec/fixtures/google/notifications/order-state-change-notification.xml
27
+ spec/fixtures/google/notifications/refund-amount-notification.xml
28
+ spec/fixtures/google/notifications/risk-information-notification.xml
29
+ spec/fixtures/google/responses/checkout-redirect.xml
30
+ spec/fixtures/google/responses/error.xml
31
+ spec/fixtures/google/responses/request-received.xml
32
+ spec/google-checkout/cart_spec.rb
33
+ spec/google-checkout/command_spec.rb
34
+ spec/google-checkout/notification_spec.rb
35
+ spec/google-checkout/response_spec.rb
36
+ spec/google-checkout_spec.rb
37
+ spec/spec_helper.rb
38
+ support/cacert.pem
39
+ examples/google_notifications_controller.rb
40
+ MIT-LICENSE.txt
data/README.txt ADDED
@@ -0,0 +1,139 @@
1
+ google-checkout
2
+ by Peter Elmore and Geoffrey Grosenbach
3
+ http://rubyforge.org/projects/google-checkout
4
+
5
+ == DESCRIPTION:
6
+
7
+ Experimental library for working with GoogleCheckout. Currently in use for
8
+ payment at http://peepcode.com.
9
+
10
+ == Installation
11
+
12
+ sudo gem install google-checkout
13
+
14
+ == What is Google Checkout?
15
+
16
+ Well, according to Google, "Google Checkout helps you increase sales. And
17
+ process them for free when you advertise with Google." What it really amounts
18
+ to is that Google will process your orders for a 10% fee and you get a little
19
+ shopping cart icon in your ad if you advertise through AdWords. The fee can
20
+ be paid by running AdWords ads on your site. You can read about it and get an
21
+ account at http://checkout.google.com/sell.
22
+
23
+ == What is google-checkout?
24
+
25
+ google-checkout is a module for working with the Google Checkout API
26
+ (http://code.google.com/apis/checkout/index.html). Specifically, if you have
27
+ a Google Checkout account, you can use this module to do things like add
28
+ "Checkout" and "Buy Now" buttons to your site.
29
+
30
+ == Brief Example
31
+
32
+ require 'rubygems'
33
+ require 'google-checkout'
34
+
35
+ merchant_id = 'Your merchant id.'
36
+ merchant_key = 'Your merchant key. Keep this a secret!'
37
+
38
+ cart = GoogleCheckout::Cart.new(merchant_id, merchant_key)
39
+ cart.add_item(:name => 'Chair', :description => 'A sturdy, wooden chair',
40
+ :price => 44.99)
41
+ puts cart.checkout_button
42
+
43
+ == Usage
44
+
45
+ First, you'll need a merchant ID and a merchant key, which you can get from
46
+ the Google Checkout site once you have an account. After you have that, you
47
+ can start writing code.
48
+
49
+ The class you'll be working with is GoogleCheckout::Cart. Of course, it
50
+ represents a cart, and you can fill it with items.
51
+
52
+ cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item1, item2)
53
+ cart.add_item item3
54
+
55
+ The items you put into the cart should be one of two types:
56
+ * A Hash containing the following
57
+ ** :name
58
+ ** :description
59
+ ** :price
60
+ ** :quantity (default 1)
61
+ ** :currency (default 'USD')
62
+ ** :regular_shipping, the shipping cost (default $0)
63
+ * Or any Object that has a method called to_google_product that returns a hash
64
+ like the one described.
65
+
66
+ Once you have a cart full of items, you can generate the XML for the API call
67
+ by calling Cart#checkout_xml, although you'll probably just want to add a
68
+ checkout button to your page with Cart#checkout_button. This method generates
69
+ HTML for a form containing a button and the hidden inputs necessary to call
70
+ Google Checkout. Cart#checkout_button has plenty of options for controlling
71
+ the look of the button. Once again, the arguments are passed as a hash,
72
+ although the defaults are usually reasonable so you might not need to pass
73
+ anything.
74
+
75
+ * :size is the size of the button, one of :small, :medium, or :large. Google
76
+ is picky about the sizes of these buttons. See GoogleCheckout::ButtonSizes
77
+ for more information. The default is :medium.
78
+ * :variant is one of 'disabled' or 'text'. 'disabled' means that the button
79
+ should be greyed-out; it is used in cases that the item you are selling
80
+ cannot be bought via Google Checkout. (There's a long list of items that
81
+ are not allowed at https://checkout.google.com/seller/content_policies.html
82
+ * :buy_or_checkout must be one of :buy_now or :checkout . This determines the
83
+ look of the button that will be displayed. The default is to use :checkout
84
+ if there are two or more items in the cart.
85
+ * :style must be one of 'white' or 'trans'. 'white' gets you a white button,
86
+ while 'trans' gets you a transparent button suitable for use on non-white
87
+ backgrounds. The default is 'white'.
88
+
89
+ cart.checkout_button :size => :small, :style => 'trans'
90
+
91
+ When users click the button, they will be taken to the Google Checkout page
92
+ with a cart full of the products you specified, and your work is done.
93
+
94
+ == Missing Features
95
+
96
+ * Level 1 integration is complete except for tax tables
97
+ * Level 2 integration has been partly implemented and is in use at http://peepcode.com.
98
+
99
+ See
100
+ http://checkout.google.com/support/sell/bin/answer.py?answer=42917&topic=8671
101
+ for more information about the two integration levels.
102
+
103
+ If there are missing features I haven't thought of, let me know.
104
+
105
+ == Bugs
106
+
107
+ No 'hard' bugs, I hope. Pete's contact information is at the bottom of the page if you find one. There may be more subjective bugs (e.g., design issues); feel free to tell me about these, too.
108
+
109
+ == Contact Information
110
+
111
+ The home page is at http://debu.gs/google-checkout . You can email me at pete
112
+ dot elmore at gmail dot com. Try to mention Google Checkout in the subject
113
+ line.
114
+
115
+ == LICENSE:
116
+
117
+ (The MIT License)
118
+
119
+ Copyright (c) 2006-2007 Peter Elmore (pete.elmore at gmail.com) and Topfunky Corporation
120
+
121
+ Permission is hereby granted, free of charge, to any person obtaining
122
+ a copy of this software and associated documentation files (the
123
+ 'Software'), to deal in the Software without restriction, including
124
+ without limitation the rights to use, copy, modify, merge, publish,
125
+ distribute, sublicense, and/or sell copies of the Software, and to
126
+ permit persons to whom the Software is furnished to do so, subject to
127
+ the following conditions:
128
+
129
+ The above copyright notice and this permission notice shall be
130
+ included in all copies or substantial portions of the Software.
131
+
132
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
133
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
134
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
135
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
136
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
137
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
138
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
139
+
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ $:.unshift(File.dirname(__FILE__) + "/lib")
4
+ require 'google-checkout'
5
+
6
+ Hoe.new('google-checkout', GoogleCheckout::VERSION) do |p|
7
+ p.name = "google-checkout"
8
+ p.author = ["Peter Elmore", "Geoffrey Grosenbach"]
9
+ p.email = 'boss@topfunky.com'
10
+ p.summary = "An experimental library for sending payment requests to Google Checkout."
11
+ p.description = p.paragraphs_of('README.txt', 1..1).join("\n\n")
12
+ p.url = "http://rubyforge.org/projects/google-checkout"
13
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+ p.remote_rdoc_dir = '' # Release docs to root
15
+ p.extra_deps = ['ruby-hmac']
16
+ end
17
+
18
+ desc "Run specs"
19
+ task :default do
20
+ system 'spec spec --format specdoc --color'
21
+ end
@@ -0,0 +1,159 @@
1
+ ##
2
+ # Skeleton for handing Level 2 notifications from GoogleCheckout with Rails.
3
+ #
4
+ # You'll need to write the individual handlers. SSL is required.
5
+ #
6
+ # SAMPLE ONLY! Modify for your own use. Extra error handling may be needed.
7
+
8
+ class GoogleNotificationsController < ApplicationController
9
+
10
+ before_filter :verify_access
11
+ after_filter :log_google_notification
12
+
13
+ ##
14
+ # Google calls this with notifications.
15
+
16
+ def create
17
+ @notification = GoogleCheckout::Notification.parse(request.raw_post)
18
+
19
+ case @notification
20
+ when GoogleCheckout::NewOrderNotification
21
+ handle_new_order_notification(@notification)
22
+
23
+ when GoogleCheckout::OrderStateChangeNotification
24
+ handle_order_state_change_notification(@notification)
25
+
26
+ when GoogleCheckout::RiskInformationNotification
27
+ handle_risk_information_notification(@notification)
28
+
29
+ when GoogleCheckout::ChargeAmountNotification
30
+ handle_charge_amount_notification(@notification)
31
+
32
+ when GoogleCheckout::AuthorizationAmountNotification
33
+ handle_authorization_amount_notification(@notification)
34
+
35
+ when GoogleCheckout::ChargebackAmountNotification
36
+ handle_chargeback_amount_notification(@notification)
37
+
38
+ when GoogleCheckout::RefundAmountNotification
39
+ handle_refund_amount_notification(@notification)
40
+ end
41
+
42
+ render :xml => @notification.acknowledgment_xml
43
+ end
44
+
45
+ private
46
+
47
+ ##
48
+ # Use basic authentication in my realm to get a user object.
49
+ # Since this is a security filter - return false if the user is not authenticated.
50
+
51
+ def verify_access
52
+ return false unless (request.ssl? || (RAILS_ENV == 'development') || (RAILS_ENV == 'test'))
53
+ authenticate_or_request_with_http_basic("PeepCode") do |merchant_id, merchant_key|
54
+ (merchant_id == GOOGLE_ID) && (merchant_key == GOOGLE_KEY)
55
+ end
56
+ end
57
+
58
+ ##
59
+ #
60
+
61
+ def log_google_notification
62
+ # TODO Write to your log or to a DB table
63
+ end
64
+
65
+ ##
66
+ #
67
+
68
+ def handle_new_order_notification(notification)
69
+ logger.info "Got NewOrderNotification"
70
+
71
+ # NOTE You should have passed your own order number to Google when
72
+ # making the initial order. Subsequent notifications will use
73
+ # Google's order number instead.
74
+ @order = Order.find_by_order_number(notification.my_order_number)
75
+ if @order
76
+ # NOTE You may want to check the amount being charged vs. the amount
77
+ # you expected the user to pay.
78
+
79
+ @order.google_order_number = notification.google_order_number
80
+ @order.email = notification.email
81
+
82
+ # Fee is 20 cents plus 2% of total.
83
+ @order.fee_cents = (20 + (notification.order_total.cents * 0.02)).round
84
+ @order.gross_cents = notification.order_total.cents
85
+ @order.net_cents = @order.gross_cents - @order.fee_cents
86
+
87
+ # NOTE Also of interest is notification.email_allowed, a boolean
88
+ @order.save
89
+ @order.new_order!
90
+ end
91
+ end
92
+
93
+ ##
94
+ #
95
+
96
+ def handle_order_state_change_notification(notification)
97
+ @order = Order.find_for_notification(notification)
98
+ @order.update_attribute(:google_state, notification.state)
99
+
100
+ case notification.state
101
+ when "REVIEWING" # Initial state of orders. Rarely seen by client.
102
+
103
+ when "CHARGEABLE" # You can now charge the customer for the order.
104
+ @order.chargeable!
105
+ when "CHARGING" # Google is charging the customer.
106
+ @order.charging!
107
+ when "CHARGED" # You have charged the customer.
108
+ @order.charged!
109
+ when "PAYMENT_DECLINED" # Google was unable to charge the client
110
+ @order.denied!
111
+ when "CANCELLED" # Order was cancelled by the merchant
112
+ @order.denied!
113
+ @order.update_attribute(:payment_note, notification.reason) rescue nil
114
+ when "CANCELLED_BY_GOOGLE" # Order was cancelled by Google
115
+ @order.denied!
116
+ # notification.reason
117
+ end
118
+ end
119
+
120
+ ##
121
+ #
122
+
123
+ def handle_risk_information_notification(notification)
124
+ logger.info "Got RiskInformationNotification"
125
+
126
+ @order = Order.find_for_notification(notification)
127
+ @order.risk!
128
+
129
+ # TODO You need to ping Google after this to trigger the next state.
130
+ # Do this in the model, but for reference, here's the basic code.
131
+ if @order.google_state == "CHARGEABLE"
132
+ charge_order_command = GoogleCheckout::ChargeOrder.new(GOOGLE_ID, GOOGLE_KEY, @order.google_order_number)
133
+ # To string, to float in order to get a float representation of the money
134
+ charge_order_command.amount = total_price.to_s.to_f
135
+ # Will throw error on failure
136
+ notification = charge_order_command.post
137
+ else
138
+ logger.error("Order was not in CHARGEABLE state")
139
+ end
140
+ end
141
+
142
+ ##
143
+ #
144
+
145
+ def handle_charge_amount_notification(notification)
146
+ @order = Order.find_for_notification(notification)
147
+ @order.charge!
148
+ end
149
+
150
+ ##
151
+ #
152
+
153
+ def handle_refund_amount_notification(notification)
154
+ # NOTE Notification includes amount refunded.
155
+ @order = Order.find_for_notification(notification)
156
+ @order.refund!
157
+ end
158
+
159
+ end
@@ -0,0 +1,24 @@
1
+
2
+ class Hpricot::Elements
3
+
4
+ ##
5
+ # Assume a Google standard money node with a currency attribute.
6
+ #
7
+ # Returns a Ruby Money object.
8
+
9
+ def to_money
10
+ dollar_amount = inner_html
11
+ cents = (dollar_amount.to_f * 100).round
12
+ currency = first[:currency]
13
+ Money.new(cents, currency)
14
+ end
15
+
16
+ ##
17
+ # Return boolean true if the value of an element is the
18
+ # string 'true'.
19
+
20
+ def to_boolean
21
+ inner_html == 'true'
22
+ end
23
+
24
+ end
@@ -0,0 +1,61 @@
1
+ ##
2
+ # Ref:
3
+ #
4
+ # https://sandbox.google.com/checkout/cws/v2/Merchant/MERCHANT_ID/merchantCheckout
5
+ # https://checkout.google.com/cws/v2/Merchant/MERCHANT_ID/merchantCheckout
6
+
7
+ $: << File.dirname(__FILE__)
8
+ $: << File.dirname(__FILE__) + "/vendor/ruby-hmac/lib"
9
+
10
+ require 'rubygems'
11
+
12
+ require 'hmac-sha1'
13
+ require 'base64'
14
+ require 'builder/xmlmarkup'
15
+ require 'hpricot'
16
+ require 'money'
17
+ require 'net/https'
18
+ require 'active_support'
19
+
20
+ require 'duck_punches/hpricot'
21
+ require 'google-checkout/notification'
22
+ require 'google-checkout/command'
23
+ require 'google-checkout/cart'
24
+
25
+ ##
26
+ # TODO
27
+ #
28
+ # * Analytics integration
29
+ # http://code.google.com/apis/checkout/developer/checkout_analytics_integration.html
30
+
31
+ module GoogleCheckout
32
+
33
+ VERSION = '0.2.0'
34
+
35
+ @@live_system = true
36
+
37
+ ##
38
+ # Submit commands to the Google Checkout test servers.
39
+
40
+ def self.use_sandbox
41
+ @@live_system = false
42
+ end
43
+
44
+ ##
45
+ # The default.
46
+
47
+ def self.use_production
48
+ @@live_system = true
49
+ end
50
+
51
+ def self.sandbox?
52
+ !@@live_system
53
+ end
54
+
55
+ def self.production?
56
+ @@live_system
57
+ end
58
+
59
+ class APIError < Exception; end
60
+
61
+ end