mlins-google-checkout 0.0.2

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 (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