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,7 @@
1
+ require 'google-checkout/geography/area'
2
+
3
+ require 'google-checkout/geography/world'
4
+ require 'google-checkout/geography/postal'
5
+ require 'google-checkout/geography/us_country'
6
+ require 'google-checkout/geography/us_state'
7
+ require 'google-checkout/geography/us_zip'
@@ -0,0 +1,11 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class Area
4
+
5
+ def to_xml
6
+ raise NotImplementedError
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class Postal < Area
4
+
5
+ attr_accessor :country, :postal_pattern
6
+
7
+ def initialize(country, postal_pattern=nil)
8
+ @country = country
9
+ @postal_pattern = postal_pattern
10
+ end
11
+
12
+ def to_xml
13
+ xml = Builder::XmlMarkup.new
14
+ xml.tag!('postal-area') do
15
+ xml.tag!('country-code') do
16
+ xml.text! @country
17
+ end
18
+ xml.tag!('postal-code-pattern') do
19
+ xml.text! @postal_pattern
20
+ end if @postal_pattern
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class UsCountry < Area
4
+
5
+ VALID_REGIONS = [:continental_48, :full_50_states, :all]
6
+
7
+ attr_accessor :region
8
+
9
+ def initialize(region)
10
+ self.region = region
11
+ end
12
+
13
+ def to_xml
14
+ unless VALID_REGIONS.include?(@region.to_sym)
15
+ raise ArgumentError,
16
+ ":#{@region.to_s} is not a valid region. You may use :continental_48, :full_50_states or :all"
17
+ end
18
+ xml = Builder::XmlMarkup.new
19
+ xml.tag!('us-country-area', 'country-area' => @region.to_s.upcase)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class UsState < Area
4
+
5
+ attr_accessor :state
6
+
7
+ def initialize(state)
8
+ @state = state
9
+ end
10
+
11
+ def to_xml
12
+ xml = Builder::XmlMarkup.new
13
+ xml.tag!('us-state-area') do
14
+ xml.tag!('state') do
15
+ xml.text! @state
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class UsZip < Area
4
+
5
+ attr_accessor :zip_pattern
6
+
7
+ def initialize(zip_pattern)
8
+ @zip_pattern = zip_pattern
9
+ end
10
+
11
+ def to_xml
12
+ xml = Builder::XmlMarkup.new
13
+ xml.tag!('us-zip-area') do
14
+ xml.tag!('zip-pattern') do
15
+ xml.text! @zip_pattern
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ module GoogleCheckout
2
+ module Geography
3
+ class World < Area
4
+
5
+ def to_xml
6
+ xml = Builder::XmlMarkup.new
7
+ xml.tag!('world-area')
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module GoogleCheckout
2
+ class MerchantCalculation
3
+
4
+ attr_accessor :doc
5
+
6
+ def self.parse(raw_xml)
7
+ doc = Hpricot.XML(raw_xml)
8
+ return new(doc)
9
+ end
10
+
11
+ def initialize(doc) # :nodoc:
12
+ @doc = doc
13
+ end
14
+
15
+ def address_id
16
+ @doc.at('anonymous-address').attributes['id']
17
+ end
18
+
19
+ def method_missing(method_name, *args)
20
+ element_name = method_name.to_s.gsub(/_/, '-')
21
+ if element = (@doc.at element_name)
22
+ if element.respond_to?(:inner_html)
23
+ return element.inner_html
24
+ end
25
+ end
26
+ super
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,212 @@
1
+
2
+ module GoogleCheckout
3
+
4
+ ##
5
+ # Base notification class. Parses incoming XML and returns a class
6
+ # matching the kind of notification being received.
7
+ #
8
+ # This makes it easy to handle events in your code.
9
+ #
10
+ # notification = GoogleCheckout::Notification.parse(request.raw_post)
11
+ # case notification
12
+ # when GoogleCheckout::NewOrderNotification
13
+ # do_something_with_new_order
14
+ # end
15
+ #
16
+ # TODO Document field access and Hpricot object access.
17
+ #
18
+ # For the details, see http://code.google.com/apis/checkout/developer/index.html
19
+
20
+ class Notification
21
+
22
+ # The Hpricot XML document received from Google.
23
+ attr_accessor :doc
24
+
25
+ ##
26
+ # The entry point for notifications.
27
+ #
28
+ # Returns a corresponding notification object based on
29
+ # the XML received.
30
+
31
+ def self.parse(raw_xml)
32
+ doc = Hpricot.XML(raw_xml)
33
+
34
+ # Convert +request-received+ to +request_received+,
35
+ # then to a +RequestReceived+ object of the proper class
36
+ # which will be created and returned.
37
+ const_name = ActiveSupport::Inflector.camelize(doc.root.name.gsub('-', '_'))
38
+ if GoogleCheckout.const_get(const_name)
39
+ return GoogleCheckout.const_get(const_name).new(doc)
40
+ end
41
+ end
42
+
43
+ def initialize(doc) # :nodoc:
44
+ @doc = doc
45
+ end
46
+
47
+ ##
48
+ # Returns the financial-order-state (or new-financial-order-state).
49
+ #
50
+ # This is a shortcut since this state will be accessed frequently.
51
+ #
52
+ # The fulfillment-order-state (and variations) can be accessed
53
+ # with the more explicit syntax:
54
+ #
55
+ # notification.fulfillment_order_state
56
+ #
57
+ # The following is from http://code.google.com/apis/checkout/developer/index.html
58
+ #
59
+ # The <financial-order-state> tag identifies the financial status of an order. Valid values for this tag are:
60
+ #
61
+ # REVIEWING - Google Checkout is reviewing the order.
62
+ # CHARGEABLE - The order is ready to be charged.
63
+ # CHARGING - The order is being charged; you may not refund or cancel an
64
+ # order until is the charge is completed.
65
+ # CHARGED - The order has been successfully charged; if the order was
66
+ # only partially charged, the buyer's account page will
67
+ # reflect the partial charge.
68
+ # PAYMENT_DECLINED - The charge attempt failed.
69
+ # CANCELLED - The seller canceled the order; an order's financial state
70
+ # cannot be changed after the order is canceled.
71
+ # CANCELLED_BY_GOOGLE - Google canceled the order. Google may cancel
72
+ # orders due to a failed charge without a replacement credit
73
+ # card being provided within a set period of time or due to a
74
+ # failed risk check. If Google cancels an order, you will be
75
+ # notified of the reason the order was canceled in the <reason>
76
+ # tag of an <order-state-change-notification>.
77
+ #
78
+ # Please see the Order States section for more information about these states.
79
+
80
+ def state
81
+ if (@doc.at 'financial-order-state')
82
+ return (@doc/'financial-order-state').inner_html
83
+ elsif (@doc.at 'new-financial-order-state')
84
+ return (@doc/'new-financial-order-state').inner_html
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Returns the serial number from the root element.
90
+
91
+ def serial_number
92
+ doc.root['serial-number']
93
+ end
94
+
95
+ ##
96
+ # Returns an XML string that can be sent back to Google to
97
+ # communicate successful receipt of the notification.
98
+
99
+ def acknowledgment_xml
100
+ xml = Builder::XmlMarkup.new
101
+ xml.instruct!
102
+ @xml = xml.tag!('notification-acknowledgment', {
103
+ :xmlns => "http://checkout.google.com/schema/2"
104
+ })
105
+ @xml
106
+ end
107
+
108
+ ##
109
+ # Returns true if this is a GoogleCheckout::Error object.
110
+
111
+ def error?
112
+ self.class == GoogleCheckout::Error
113
+ end
114
+
115
+ ##
116
+ # Take requests for an XML element and returns its value.
117
+ #
118
+ # notification.google_order_number
119
+ # => Returns value of '<google-order-number>'
120
+ #
121
+ # Because of how Hpricot#at works, it will even dig into subtags
122
+ # and return the value of the first matching tag. For example,
123
+ # there is an +email+ field in +buyer-shipping-address+ and also
124
+ # in +buyer-billing-address+, but only the first will be returned.
125
+ #
126
+ # If you want to get at a value explicitly, use +notification.doc+
127
+ # and search the Hpricot document manually.
128
+
129
+ def method_missing(method_name, *args)
130
+ element_name = method_name.to_s.gsub(/_/, '-')
131
+ if element = (@doc.at element_name)
132
+ if element.respond_to?(:inner_html)
133
+ return element.inner_html
134
+ end
135
+ end
136
+ super
137
+ end
138
+
139
+ end
140
+
141
+ class AuthorizationAmountNotification < Notification; end
142
+
143
+ class ChargeAmountNotification < Notification
144
+
145
+ def latest_charge_amount
146
+ (@doc/"latest-charge-amount").to_money
147
+ end
148
+
149
+ def total_charge_amount
150
+ (@doc/"total-charge-amount").to_money
151
+ end
152
+
153
+ end
154
+
155
+ class ChargebackAmountNotification < Notification; end
156
+
157
+ class NewOrderNotification < Notification
158
+
159
+ ##
160
+ # Returns a Money object representing the total price of the order.
161
+
162
+ def order_total
163
+ (@doc/"order-total").to_money
164
+ end
165
+
166
+ ##
167
+ # Returns a Money object representing the total tax added.
168
+
169
+ def total_tax
170
+ (@doc/"total-tax").to_money
171
+ end
172
+
173
+ ##
174
+ # Returns true if the buyer wants to received marketing emails.
175
+
176
+ def email_allowed
177
+ (@doc/"buyer-marketing-preferences"/"email-allowed").to_boolean
178
+ end
179
+
180
+ end
181
+
182
+ class OrderStateChangeNotification < Notification; end
183
+
184
+ class RefundAmountNotification < Notification; end
185
+
186
+ class RiskInformationNotification < Notification; end
187
+
188
+ class CheckoutRedirect < Notification
189
+
190
+ ##
191
+ # Returns redirect-url with ampersands escaped, as specified by Google API docs.
192
+
193
+ def redirect_url
194
+ (@doc/"redirect-url").inner_html.gsub(/&amp;/, '&')
195
+ end
196
+
197
+ end
198
+
199
+ class Error < Notification
200
+
201
+ ##
202
+ # Alias for +error_message+
203
+
204
+ def message
205
+ (@doc/'error-message').inner_html
206
+ end
207
+
208
+ end
209
+
210
+ class RequestReceived < Notification; end
211
+
212
+ end
@@ -0,0 +1,8 @@
1
+ require 'google-checkout/shipping/method'
2
+
3
+ require 'google-checkout/shipping/restrictions'
4
+ require 'google-checkout/shipping/filters'
5
+
6
+ require 'google-checkout/shipping/pickup'
7
+ require 'google-checkout/shipping/flat_rate'
8
+ require 'google-checkout/shipping/merchant_calculated'
@@ -0,0 +1,32 @@
1
+ module GoogleCheckout
2
+ module Shipping
3
+ module Filters
4
+
5
+ attr_accessor :address_filters
6
+
7
+ def address_filters?
8
+ @address_filters != {:allowed => [], :excluded => [], :allow_us_po_box => true}
9
+ end
10
+
11
+ def address_filters_xml
12
+ xml = Builder::XmlMarkup.new
13
+ xml.tag!('address-filters') do
14
+ xml.tag!('allowed-areas') do
15
+ @address_filters[:allowed].each do |area|
16
+ xml << area.to_xml
17
+ end
18
+ end unless @address_filters[:allowed].empty?
19
+ xml.tag!('excluded-areas') do
20
+ @address_filters[:excluded].each do |area|
21
+ xml << area.to_xml
22
+ end
23
+ end unless @address_filters[:excluded].empty?
24
+ xml.tag!('allow-us-po-box') do
25
+ xml.text! 'false'
26
+ end unless @address_filters[:allow_us_po_box]
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ module GoogleCheckout
2
+ module Shipping
3
+ class FlatRate < Method
4
+
5
+ include Restrictions
6
+
7
+ attr_accessor :name, :price, :currency
8
+
9
+ def initialize(name, price, currency = 'USD')
10
+ @name = name
11
+ @price = price
12
+ @currency = currency
13
+ @shipping_restrictions = {:allowed => [], :excluded => [], :allow_us_po_box => true}
14
+ end
15
+
16
+ def to_xml
17
+ xml = Builder::XmlMarkup.new
18
+ xml.tag!('flat-rate-shipping', :name => name) do
19
+ xml.tag!('price', price, :currency => currency)
20
+ xml << shipping_restrictions_xml if shipping_restrictions?
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end