mlins-google-checkout 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/History.txt +14 -0
  2. data/MIT-LICENSE.txt +23 -0
  3. data/README.txt +143 -0
  4. data/Rakefile +34 -0
  5. data/VERSION.yml +4 -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 +65 -0
  9. data/lib/google-checkout/cart.rb +302 -0
  10. data/lib/google-checkout/command.rb +191 -0
  11. data/lib/google-checkout/geography.rb +7 -0
  12. data/lib/google-checkout/geography/area.rb +11 -0
  13. data/lib/google-checkout/geography/postal.rb +26 -0
  14. data/lib/google-checkout/geography/us_country.rb +24 -0
  15. data/lib/google-checkout/geography/us_state.rb +22 -0
  16. data/lib/google-checkout/geography/us_zip.rb +22 -0
  17. data/lib/google-checkout/geography/world.rb +12 -0
  18. data/lib/google-checkout/merchant_calculation.rb +30 -0
  19. data/lib/google-checkout/notification.rb +212 -0
  20. data/lib/google-checkout/shipping.rb +8 -0
  21. data/lib/google-checkout/shipping/filters.rb +32 -0
  22. data/lib/google-checkout/shipping/flat_rate.rb +26 -0
  23. data/lib/google-checkout/shipping/merchant_calculated.rb +29 -0
  24. data/lib/google-checkout/shipping/method.rb +11 -0
  25. data/lib/google-checkout/shipping/pickup.rb +22 -0
  26. data/lib/google-checkout/shipping/restrictions.rb +32 -0
  27. data/spec/fixtures/google/checkout-shopping-cart.xml +22 -0
  28. data/spec/fixtures/google/commands/add-merchant-order-number.xml +5 -0
  29. data/spec/fixtures/google/commands/add-tracking-data.xml +8 -0
  30. data/spec/fixtures/google/commands/archive-order.xml +3 -0
  31. data/spec/fixtures/google/commands/authorize-order.xml +2 -0
  32. data/spec/fixtures/google/commands/cancel-order.xml +5 -0
  33. data/spec/fixtures/google/commands/charge-order.xml +4 -0
  34. data/spec/fixtures/google/commands/deliver-order.xml +9 -0
  35. data/spec/fixtures/google/commands/process-order.xml +2 -0
  36. data/spec/fixtures/google/commands/refund-order.xml +6 -0
  37. data/spec/fixtures/google/commands/send-buyer-message.xml +7 -0
  38. data/spec/fixtures/google/commands/unarchive-order.xml +2 -0
  39. data/spec/fixtures/google/merchant_calculations/shipping.xml +40 -0
  40. data/spec/fixtures/google/notifications/authorization-amount-notification.xml +10 -0
  41. data/spec/fixtures/google/notifications/charge-amount-notification.xml +8 -0
  42. data/spec/fixtures/google/notifications/chargeback-amount-notification.xml +8 -0
  43. data/spec/fixtures/google/notifications/new-order-notification.xml +85 -0
  44. data/spec/fixtures/google/notifications/order-state-change-notification.xml +11 -0
  45. data/spec/fixtures/google/notifications/refund-amount-notification.xml +8 -0
  46. data/spec/fixtures/google/notifications/risk-information-notification.xml +23 -0
  47. data/spec/fixtures/google/responses/checkout-redirect.xml +5 -0
  48. data/spec/fixtures/google/responses/error.xml +5 -0
  49. data/spec/fixtures/google/responses/request-received.xml +3 -0
  50. data/spec/google-checkout/cart_spec.rb +110 -0
  51. data/spec/google-checkout/command_spec.rb +131 -0
  52. data/spec/google-checkout/geography/postal_spec.rb +26 -0
  53. data/spec/google-checkout/geography/us_country_spec.rb +26 -0
  54. data/spec/google-checkout/geography/us_state_spec.rb +11 -0
  55. data/spec/google-checkout/geography/us_zip_spec.rb +11 -0
  56. data/spec/google-checkout/geography/world_spec.rb +12 -0
  57. data/spec/google-checkout/merchant_calculation_spec.rb +17 -0
  58. data/spec/google-checkout/notification_spec.rb +175 -0
  59. data/spec/google-checkout/response_spec.rb +49 -0
  60. data/spec/google-checkout/shipping/flat_rate_spec.rb +46 -0
  61. data/spec/google-checkout/shipping/merchant_calculated_spec.rb +70 -0
  62. data/spec/google-checkout/shipping/pickup_spec.rb +22 -0
  63. data/spec/google-checkout_spec.rb +15 -0
  64. data/spec/spec_helper.rb +47 -0
  65. data/support/cacert.pem +7815 -0
  66. metadata +140 -0
@@ -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.
@@ -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
+
@@ -0,0 +1,143 @@
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
+ Or straight from the source at
15
+
16
+ sudo gem install topfunky-google-checkout --source http://gems.github.com
17
+
18
+ == What is Google Checkout?
19
+
20
+ Well, according to Google, "Google Checkout helps you increase sales. And
21
+ process them for free when you advertise with Google." What it really amounts
22
+ to is that Google will process your orders for a 10% fee and you get a little
23
+ shopping cart icon in your ad if you advertise through AdWords. The fee can
24
+ be paid by running AdWords ads on your site. You can read about it and get an
25
+ account at http://checkout.google.com/sell.
26
+
27
+ == What is google-checkout?
28
+
29
+ google-checkout is a module for working with the Google Checkout API
30
+ (http://code.google.com/apis/checkout/index.html). Specifically, if you have
31
+ a Google Checkout account, you can use this module to do things like add
32
+ "Checkout" and "Buy Now" buttons to your site.
33
+
34
+ == Brief Example
35
+
36
+ require 'rubygems'
37
+ require 'google-checkout'
38
+
39
+ merchant_id = 'Your merchant id.'
40
+ merchant_key = 'Your merchant key. Keep this a secret!'
41
+
42
+ cart = GoogleCheckout::Cart.new(merchant_id, merchant_key)
43
+ cart.add_item(:name => 'Chair', :description => 'A sturdy, wooden chair',
44
+ :price => 44.99)
45
+ puts cart.checkout_button
46
+
47
+ == Usage
48
+
49
+ First, you'll need a merchant ID and a merchant key, which you can get from
50
+ the Google Checkout site once you have an account. After you have that, you
51
+ can start writing code.
52
+
53
+ The class you'll be working with is GoogleCheckout::Cart. Of course, it
54
+ represents a cart, and you can fill it with items.
55
+
56
+ cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item1, item2)
57
+ cart.add_item item3
58
+
59
+ The items you put into the cart should be one of two types:
60
+ * A Hash containing the following
61
+ ** :name
62
+ ** :description
63
+ ** :price
64
+ ** :quantity (default 1)
65
+ ** :currency (default 'USD')
66
+ ** :regular_shipping, the shipping cost (default $0)
67
+ * Or any Object that has a method called to_google_product that returns a hash
68
+ like the one described.
69
+
70
+ Once you have a cart full of items, you can generate the XML for the API call
71
+ by calling Cart#checkout_xml, although you'll probably just want to add a
72
+ checkout button to your page with Cart#checkout_button. This method generates
73
+ HTML for a form containing a button and the hidden inputs necessary to call
74
+ Google Checkout. Cart#checkout_button has plenty of options for controlling
75
+ the look of the button. Once again, the arguments are passed as a hash,
76
+ although the defaults are usually reasonable so you might not need to pass
77
+ anything.
78
+
79
+ * :size is the size of the button, one of :small, :medium, or :large. Google
80
+ is picky about the sizes of these buttons. See GoogleCheckout::ButtonSizes
81
+ for more information. The default is :medium.
82
+ * :variant is one of 'disabled' or 'text'. 'disabled' means that the button
83
+ should be greyed-out; it is used in cases that the item you are selling
84
+ cannot be bought via Google Checkout. (There's a long list of items that
85
+ are not allowed at https://checkout.google.com/seller/content_policies.html
86
+ * :buy_or_checkout must be one of :buy_now or :checkout . This determines the
87
+ look of the button that will be displayed. The default is to use :checkout
88
+ if there are two or more items in the cart.
89
+ * :style must be one of 'white' or 'trans'. 'white' gets you a white button,
90
+ while 'trans' gets you a transparent button suitable for use on non-white
91
+ backgrounds. The default is 'white'.
92
+
93
+ cart.checkout_button :size => :small, :style => 'trans'
94
+
95
+ When users click the button, they will be taken to the Google Checkout page
96
+ with a cart full of the products you specified, and your work is done.
97
+
98
+ == Missing Features
99
+
100
+ * Level 1 integration is complete except for tax tables
101
+ * Level 2 integration has been partly implemented and is in use at http://peepcode.com.
102
+
103
+ See
104
+ http://checkout.google.com/support/sell/bin/answer.py?answer=42917&topic=8671
105
+ for more information about the two integration levels.
106
+
107
+ If there are missing features I haven't thought of, let me know.
108
+
109
+ == Bugs
110
+
111
+ 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.
112
+
113
+ == Contact Information
114
+
115
+ The home page is at http://debu.gs/google-checkout . You can email me at pete
116
+ dot elmore at gmail dot com. Try to mention Google Checkout in the subject
117
+ line.
118
+
119
+ == LICENSE:
120
+
121
+ (The MIT License)
122
+
123
+ Copyright (c) 2006-2007 Peter Elmore (pete.elmore at gmail.com) and Topfunky Corporation
124
+
125
+ Permission is hereby granted, free of charge, to any person obtaining
126
+ a copy of this software and associated documentation files (the
127
+ 'Software'), to deal in the Software without restriction, including
128
+ without limitation the rights to use, copy, modify, merge, publish,
129
+ distribute, sublicense, and/or sell copies of the Software, and to
130
+ permit persons to whom the Software is furnished to do so, subject to
131
+ the following conditions:
132
+
133
+ The above copyright notice and this permission notice shall be
134
+ included in all copies or substantial portions of the Software.
135
+
136
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
137
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
138
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
139
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
140
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
141
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
142
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
143
+
@@ -0,0 +1,34 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc 'Run the specs'
6
+ Spec::Rake::SpecTask.new(:spec) do |t|
7
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
8
+ t.spec_files = FileList['spec/**/*_spec.rb']
9
+ end
10
+
11
+ Rake::RDocTask.new do |t|
12
+ t.rdoc_dir = 'doc'
13
+ t.rdoc_files.include('README')
14
+ t.rdoc_files.include('lib/**/*.rb')
15
+ t.options << '--inline-source'
16
+ t.options << '--all'
17
+ t.options << '--line-numbers'
18
+ end
19
+
20
+ begin
21
+ require 'jeweler'
22
+ Jeweler::Tasks.new do |s|
23
+ s.name = "google-checkout"
24
+ s.summary = "An experimental library for sending payment requests to Google Checkout."
25
+ s.email = "mattlins@gmail.com"
26
+ s.homepage = "http://github.com/mlins/google-checkout/"
27
+ s.description = "An experimental library for sending payment requests to Google Checkout."
28
+ s.authors = ["Peter Elmore", "Geoffrey Grosenbach", "Matt Lins"]
29
+ s.files = FileList["[A-Z]*", "{lib,spec,support,examples}/**/*"]
30
+ s.add_dependency 'ruby-hmac'
31
+ end
32
+ rescue LoadError
33
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
34
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ major: 0
3
+ patch: 2
4
+ minor: 0
@@ -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,65 @@
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/merchant_calculation'
23
+ require 'google-checkout/command'
24
+ require 'google-checkout/cart'
25
+
26
+ require 'google-checkout/shipping'
27
+ require 'google-checkout/geography'
28
+
29
+ ##
30
+ # TODO
31
+ #
32
+ # * Analytics integration
33
+ # http://code.google.com/apis/checkout/developer/checkout_analytics_integration.html
34
+
35
+ module GoogleCheckout
36
+
37
+ #VERSION = '0.2.0'
38
+
39
+ @@live_system = true
40
+
41
+ ##
42
+ # Submit commands to the Google Checkout test servers.
43
+
44
+ def self.use_sandbox
45
+ @@live_system = false
46
+ end
47
+
48
+ ##
49
+ # The default.
50
+
51
+ def self.use_production
52
+ @@live_system = true
53
+ end
54
+
55
+ def self.sandbox?
56
+ !@@live_system
57
+ end
58
+
59
+ def self.production?
60
+ @@live_system
61
+ end
62
+
63
+ class APIError < Exception; end
64
+
65
+ end