google4r 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +5 -0
- data/LICENSE +22 -0
- data/README +75 -0
- data/lib/google4r/checkout.rb +36 -0
- data/lib/google4r/checkout/commands.rb +267 -0
- data/lib/google4r/checkout/frontend.rb +100 -0
- data/lib/google4r/checkout/notifications.rb +533 -0
- data/lib/google4r/checkout/shared.rb +501 -0
- data/lib/google4r/checkout/xml_generation.rb +271 -0
- data/lib/google4r/maps.rb +174 -0
- data/test/checkout/integration/checkout_command_test.rb +103 -0
- data/test/checkout/unit/address_test.rb +131 -0
- data/test/checkout/unit/area_test.rb +41 -0
- data/test/checkout/unit/checkout_command_test.rb +112 -0
- data/test/checkout/unit/checkout_command_xml_generator_test.rb +187 -0
- data/test/checkout/unit/command_test.rb +126 -0
- data/test/checkout/unit/flat_rate_shipping_test.rb +114 -0
- data/test/checkout/unit/frontend_test.rb +63 -0
- data/test/checkout/unit/item_test.rb +159 -0
- data/test/checkout/unit/marketing_preferences_test.rb +65 -0
- data/test/checkout/unit/merchant_code_test.rb +122 -0
- data/test/checkout/unit/new_order_notification_test.rb +115 -0
- data/test/checkout/unit/notification_acknowledgement_test.rb +43 -0
- data/test/checkout/unit/notification_handler_test.rb +93 -0
- data/test/checkout/unit/order_adjustment_test.rb +95 -0
- data/test/checkout/unit/order_state_change_notification_test.rb +159 -0
- data/test/checkout/unit/pickup_shipping_test.rb +70 -0
- data/test/checkout/unit/private_data_parser_test.rb +68 -0
- data/test/checkout/unit/shipping_adjustment_test.rb +100 -0
- data/test/checkout/unit/shipping_method_test.rb +41 -0
- data/test/checkout/unit/shopping_cart_test.rb +146 -0
- data/test/checkout/unit/tax_rule_test.rb +65 -0
- data/test/checkout/unit/tax_table_test.rb +82 -0
- data/test/checkout/unit/us_country_area_test.rb +76 -0
- data/test/checkout/unit/us_state_area_test.rb +70 -0
- data/test/checkout/unit/us_zip_area_test.rb +66 -0
- data/test/maps/geocoder_test.rb +143 -0
- data/var/cacert.pem +7815 -0
- metadata +100 -0
@@ -0,0 +1,271 @@
|
|
1
|
+
#--
|
2
|
+
# Project: google4r
|
3
|
+
# File: lib/google4r/checkout/xml_generation.rb
|
4
|
+
# Author: Manuel Holtgrewe <purestorm at ggnore dot net>
|
5
|
+
# Copyright: (c) 2007 by Manuel Holtgrewe
|
6
|
+
# License: MIT License as follows:
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# "Software"), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to permit
|
13
|
+
# persons to whom the Software is furnished to do so, subject to the
|
14
|
+
# following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be included
|
17
|
+
# in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
25
|
+
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#++
|
27
|
+
# This file contains the classes that allow to persist the object hierarchies
|
28
|
+
# that are created with the Google Checkout API to XML.
|
29
|
+
|
30
|
+
require 'stringio'
|
31
|
+
require 'rexml/document'
|
32
|
+
|
33
|
+
module Google4R #:nodoc:
|
34
|
+
module Checkout #:nodoc:
|
35
|
+
# Use the CheckoutXmlGenerator to create an XML document from a CheckoutCommand
|
36
|
+
# object.
|
37
|
+
#
|
38
|
+
# Usage:
|
39
|
+
#
|
40
|
+
# checkout = CheckoutCommand.new
|
41
|
+
# # set up the CheckoutCommand
|
42
|
+
#
|
43
|
+
# generator = CheckoutCommandXmlGenerator.new(checkout)
|
44
|
+
# puts generator.generate # => "<xml? version=..."
|
45
|
+
# File.new('some.xml', 'w') { |f| f.write generator.generate }
|
46
|
+
#--
|
47
|
+
# TODO
|
48
|
+
#
|
49
|
+
# * Refactor the big, monolitic generator into smaller, easier testable ones. One
|
50
|
+
# for each major part of the resulting XML document. This will also reduce the
|
51
|
+
# overhead in generating other types of XML documents.
|
52
|
+
#++
|
53
|
+
class CheckoutCommandXmlGenerator
|
54
|
+
# Initializes the CheckoutCommandXmlGenerator with the CheckoutCommand it is
|
55
|
+
# to conver to XML.
|
56
|
+
def initialize(checkout_command)
|
57
|
+
@checkout_command = checkout_command
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates an XML document from the checkout_command passed into the constructor. Returns
|
61
|
+
# the resulting XML string.
|
62
|
+
def generate
|
63
|
+
@document = REXML::Document.new
|
64
|
+
|
65
|
+
declaration = REXML::XMLDecl.new
|
66
|
+
declaration.encoding = 'utf-8'
|
67
|
+
@document << declaration
|
68
|
+
|
69
|
+
self.process_checkout_command(@checkout_command)
|
70
|
+
|
71
|
+
io = StringIO.new
|
72
|
+
@document.write(io, 0) # TODO: Maybe replace 0 by -1 so no spaces are inserted?
|
73
|
+
return io.string
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def process_checkout_command(command)
|
79
|
+
root = @document.add_element('checkout-shopping-cart', { 'xmlns' => 'http://checkout.google.com/schema/2' })
|
80
|
+
|
81
|
+
self.process_shopping_cart(root, command.cart)
|
82
|
+
|
83
|
+
# <merchant-checkout-flow-support>
|
84
|
+
flow_element = root.add_element('checkout-flow-support').add_element('merchant-checkout-flow-support')
|
85
|
+
|
86
|
+
# <continue-shopping-url>
|
87
|
+
if not command.continue_shopping_url.nil? then
|
88
|
+
flow_element.add_element('continue-shopping-url').text = command.continue_shopping_url
|
89
|
+
end
|
90
|
+
|
91
|
+
# <edit-cart-url>
|
92
|
+
if not command.edit_cart_url.nil? then
|
93
|
+
flow_element.add_element('edit-cart-url').text = command.edit_cart_url
|
94
|
+
end
|
95
|
+
|
96
|
+
# <request-buyer-phone-number>
|
97
|
+
if not command.request_buyer_phone_number.nil? then
|
98
|
+
flow_element.add_element('request-buyer-phone-number').text =
|
99
|
+
if command.request_buyer_phone_number then
|
100
|
+
"true"
|
101
|
+
else
|
102
|
+
"false"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# <tax-tables>
|
107
|
+
command.tax_tables.each do |tax_table|
|
108
|
+
end
|
109
|
+
|
110
|
+
# <shipping-methods>
|
111
|
+
shippings_element = flow_element.add_element('shipping-methods')
|
112
|
+
command.shipping_methods.each do |shipping_method|
|
113
|
+
self.process_shipping_method(shippings_element, shipping_method)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def process_shopping_cart(parent, cart)
|
118
|
+
cart_element = parent.add_element('shopping-cart')
|
119
|
+
|
120
|
+
# add <cart-expiration> tag to the cart if a time has been added to the cart
|
121
|
+
if not cart.expires_at.nil? then
|
122
|
+
cart_element.add_element('cart-expiration').add_element('good-until-date').text =
|
123
|
+
cart.expires_at.iso8601
|
124
|
+
end
|
125
|
+
|
126
|
+
# add <merchant-private-data> to the cart if any has been set
|
127
|
+
if not cart.private_data.nil? then
|
128
|
+
self.process_hash(cart_element.add_element('merchant-private-data'), cart.private_data)
|
129
|
+
end
|
130
|
+
|
131
|
+
# process the items in the cart
|
132
|
+
items_element = cart_element.add_element('items')
|
133
|
+
cart.items.each do |item|
|
134
|
+
self.process_item(items_element, item)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Adds an <item> tag to the tag parent with the appropriate values.
|
139
|
+
def process_item(parent, item)
|
140
|
+
item_element = parent.add_element('item')
|
141
|
+
|
142
|
+
item_element.add_element('item-name').text = item.name
|
143
|
+
item_element.add_element('item-description').text = item.description
|
144
|
+
|
145
|
+
item_element.add_element('unit-price', { 'currency' => item.unit_price.currency }).text = item.unit_price.to_s
|
146
|
+
item_element.add_element('quantity').text = item.quantity.to_i
|
147
|
+
|
148
|
+
if not item.id.nil? then
|
149
|
+
item_element.add_element('merchant-item-id').text = item.id
|
150
|
+
end
|
151
|
+
|
152
|
+
if not item.private_data.nil? then
|
153
|
+
self.process_hash(item_element.add_element('merchant-private-item-data'), item.private_data)
|
154
|
+
end
|
155
|
+
|
156
|
+
# The above was easy; now we need to get the appropriate tax table for this
|
157
|
+
# item. The Item class makes sure that the table exists.
|
158
|
+
if not item.tax_table.nil? then
|
159
|
+
item_element.add_element('tax-table-selector').text = item.tax_table.name
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Adds an item for the given shipping method.
|
164
|
+
def process_shipping_method(parent, shipping_method)
|
165
|
+
if shipping_method.kind_of? PickupShipping then
|
166
|
+
process_pickup_shipping(parent, shipping_method)
|
167
|
+
elsif shipping_method.kind_of? FlatRateShipping then
|
168
|
+
process_flat_rate_shipping(parent, shipping_method)
|
169
|
+
else
|
170
|
+
raise "Unknown ShippingMethod type of #{shipping_method.inspect}!"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def process_flat_rate_shipping(parent, shipping)
|
175
|
+
element = parent.add_element('flat-rate-shipping')
|
176
|
+
element.add_attribute('name', shipping.name)
|
177
|
+
element.add_element('price', { 'currency' => shipping.price.currency }).text = shipping.price.to_s
|
178
|
+
|
179
|
+
if shipping.excluded_areas.length + shipping.allowed_areas.length > 0 then
|
180
|
+
restrictions_tag = element.add_element('shipping-restrictions')
|
181
|
+
|
182
|
+
if shipping.allowed_areas.length > 0 then
|
183
|
+
allowed_tag = restrictions_tag.add_element('allowed-areas')
|
184
|
+
|
185
|
+
shipping.allowed_areas.each do |area|
|
186
|
+
self.process_area(allowed_tag, area)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if shipping.excluded_areas.length > 0 then
|
191
|
+
excluded_tag = restrictions_tag.add_element('excluded-areas')
|
192
|
+
|
193
|
+
shipping.excluded_areas.each do |area|
|
194
|
+
self.process_area(excluded_tag, area)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def process_pickup_shipping(parent, shipping)
|
201
|
+
element = parent.add_element('pickup')
|
202
|
+
element.add_attribute('name', shipping.name)
|
203
|
+
element.add_element('price', { 'currency' => shipping.price.currency }).text = shipping.price.to_s
|
204
|
+
end
|
205
|
+
|
206
|
+
# Adds an appropriate tag for the given Area subclass instance to the parent Element.
|
207
|
+
def process_area(parent, area)
|
208
|
+
if area.kind_of? UsZipArea then
|
209
|
+
parent.add_element('us-zip-area').add_element('zip-pattern').text = area.pattern
|
210
|
+
elsif area.kind_of? UsCountryArea then
|
211
|
+
parent.add_element('us-country-area', { 'country-area' => area.area })
|
212
|
+
elsif area.kind_of? UsStateArea then
|
213
|
+
parent.add_element('us-state-area').add_element('state').text = area.state
|
214
|
+
else
|
215
|
+
raise "Area of unknown type: #{area.inspect}."
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Converts a Hash into an XML structure. The keys are converted to tag names. If
|
220
|
+
# the values are Hashs themselves then process_hash is called upon them. If the
|
221
|
+
# values are Arrays then a new element with the key's name will be created.
|
222
|
+
#
|
223
|
+
# If a value is an Array then this array will be flattened before it is processed.
|
224
|
+
# Thus, nested arrays are not allowed.
|
225
|
+
#
|
226
|
+
# === Example
|
227
|
+
#
|
228
|
+
# process_hash(parent, { 'foo' => { 'bar' => 'baz' } })
|
229
|
+
#
|
230
|
+
# # will produce a structure that is equivalent to.
|
231
|
+
#
|
232
|
+
# <foo>
|
233
|
+
# <bar>baz</bar>
|
234
|
+
# </foo>
|
235
|
+
#
|
236
|
+
#
|
237
|
+
# process_hash(parent, { 'foo' => [ { 'bar' => 'baz' }, "d'oh", 2 ] })
|
238
|
+
#
|
239
|
+
# # will produce a structure that is equivalent to.
|
240
|
+
#
|
241
|
+
# <foo>
|
242
|
+
# <bar>baz</bar>
|
243
|
+
# </foo>
|
244
|
+
# <foo>d&</foo>
|
245
|
+
# <foo>2</foo>
|
246
|
+
def process_hash(parent, hash)
|
247
|
+
hash.each do |key, value|
|
248
|
+
if value.kind_of? Array then
|
249
|
+
value.flatten.each do |arr_entry|
|
250
|
+
if arr_entry.kind_of? Hash then
|
251
|
+
self.process_hash(parent.add_element(self.str2tag_name(key.to_s)), arr_entry)
|
252
|
+
else
|
253
|
+
parent.add_element(self.str2tag_name(key.to_s)).text = arr_entry.to_s
|
254
|
+
end
|
255
|
+
end
|
256
|
+
elsif value.kind_of? Hash then
|
257
|
+
process_hash(parent.add_element(self.str2tag_name(key.to_s)), value)
|
258
|
+
else
|
259
|
+
parent.add_element(self.str2tag_name(key.to_s))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Converts a string to a valid XML tag name. Whitespace will be converted into a dash/minus
|
265
|
+
# sign, non alphanumeric characters that are neither "-" nor "_" nor ":" will be stripped.
|
266
|
+
def str2tag_name(str)
|
267
|
+
str.gsub(%r{\s}, '-').gsub(%r{[^a-zA-Z0-9\-\_:]}, '')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
#--
|
2
|
+
# Project: google4r
|
3
|
+
# File: lib/google4r/geocoder.rb
|
4
|
+
# Author: Manuel Holtgrewe <purestorm at ggnore dot net>
|
5
|
+
# Copyright: (c) 2006 by Manuel Holtgrewe
|
6
|
+
# License: MIT License as follows:
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# "Software"), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to permit
|
13
|
+
# persons to whom the Software is furnished to do so, subject to the
|
14
|
+
# following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be included
|
17
|
+
# in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
25
|
+
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#++
|
27
|
+
# This file defines the code which is necessary to access the Google Maps
|
28
|
+
# Geocoder.
|
29
|
+
|
30
|
+
require 'net/http'
|
31
|
+
require 'uri'
|
32
|
+
require 'json'
|
33
|
+
|
34
|
+
module Google4R
|
35
|
+
module Maps
|
36
|
+
# Thrown if no API has been given or it is incorrect.
|
37
|
+
class KeyException < Exception
|
38
|
+
end
|
39
|
+
|
40
|
+
# Thrown when it was impossible to connect to the server.
|
41
|
+
class ConnectionException < Exception
|
42
|
+
end
|
43
|
+
|
44
|
+
# Allows to geocode a location.
|
45
|
+
#
|
46
|
+
# Uses the Google Maps API to find information about locations specified by strings. You need
|
47
|
+
# a Google Maps API key to use the Geocoder.
|
48
|
+
#
|
49
|
+
# The result has the same format as documented in [1] (within <kml>) if it is directly converted
|
50
|
+
# into Ruby: A value consisting of nested Hashes and Arrays.
|
51
|
+
#
|
52
|
+
# After querying, you can access the last server response code using #last_status_code.
|
53
|
+
#
|
54
|
+
# Notice that you can also use Google's geolocator service to locate ZIPs by querying for the
|
55
|
+
# ZIP and country name.
|
56
|
+
#
|
57
|
+
# Usage Example:
|
58
|
+
#
|
59
|
+
# api_key = 'abcdefg'
|
60
|
+
# geocoder = Google4R::Geocoder.new(api_key)
|
61
|
+
# result = geocoder.query("1 Infinite Loop, Cupertino")
|
62
|
+
# puts result["Placemark"][0]["address"] # => "1 Infinite Loop, Cupertino, CA 95014"
|
63
|
+
#
|
64
|
+
# 1:: http://www.google.de/apis/maps/documentation/#Geocoding_Structured
|
65
|
+
class Geocoder
|
66
|
+
# Returns the last status code returned by the server.
|
67
|
+
attr_reader :last_status_code
|
68
|
+
|
69
|
+
# The hardcoded URL of Google's geolocator API.
|
70
|
+
GET_URL = 'http://maps.google.com/maps/geo?q=%s&output=%s&key=%s'.freeze
|
71
|
+
|
72
|
+
# Response code constants.
|
73
|
+
G_GEO_SUCCESS = 200
|
74
|
+
G_GEO_SERVER_ERROR = 500
|
75
|
+
G_GEO_MISSING_ADDRESS = 601
|
76
|
+
G_GEO_UNKNOWN_ADDRESS = 602
|
77
|
+
G_UNAVAILABLE_ADDRESS = 603
|
78
|
+
G_GEO_BAD_KEY = 610
|
79
|
+
|
80
|
+
# Creates a new Geocoder object. You have to supply a valid Google Maps
|
81
|
+
# API key.
|
82
|
+
def initialize(key)
|
83
|
+
@api_key = key
|
84
|
+
end
|
85
|
+
|
86
|
+
# === Parameters
|
87
|
+
#
|
88
|
+
# query:: The place to locate.
|
89
|
+
#
|
90
|
+
# === Exceptions
|
91
|
+
#
|
92
|
+
# Throws a KeyException if the key for this Geocoder instance is invalid and
|
93
|
+
# throws a ConnectionException if the Geocoder instance could not connect to
|
94
|
+
# Google's server or an server error occured.
|
95
|
+
#
|
96
|
+
# === Return Values
|
97
|
+
#
|
98
|
+
# Returns data in the same format as documented in [1]
|
99
|
+
#
|
100
|
+
# Example of returned values:
|
101
|
+
#
|
102
|
+
# {
|
103
|
+
# "name": "1600 Amphitheatre Parkway, Mountain View, CA, USA",
|
104
|
+
# "Status": {
|
105
|
+
# "code": 200,
|
106
|
+
# "request": "geocode"
|
107
|
+
# },
|
108
|
+
# "Placemark": [
|
109
|
+
# {
|
110
|
+
# "address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
|
111
|
+
# "AddressDetails": {
|
112
|
+
# "Country": {
|
113
|
+
# "CountryNameCode": "US",
|
114
|
+
# "AdministrativeArea": {
|
115
|
+
# "AdministrativeAreaName": "CA",
|
116
|
+
# "SubAdministrativeArea": {
|
117
|
+
# "SubAdministrativeAreaName": "Santa Clara",
|
118
|
+
# "Locality": {
|
119
|
+
# "LocalityName": "Mountain View",
|
120
|
+
# "Thoroughfare": {
|
121
|
+
# "ThoroughfareName": "1600 Amphitheatre Pkwy"
|
122
|
+
# },
|
123
|
+
# "PostalCode": {
|
124
|
+
# "PostalCodeNumber": "94043"
|
125
|
+
# }
|
126
|
+
# }
|
127
|
+
# }
|
128
|
+
# }
|
129
|
+
# },
|
130
|
+
# "Accuracy": 8
|
131
|
+
# },
|
132
|
+
# "Point": {
|
133
|
+
# "coordinates": [-122.083739, 37.423021, 0]
|
134
|
+
# }
|
135
|
+
# }
|
136
|
+
# ]
|
137
|
+
# }
|
138
|
+
#
|
139
|
+
# 1:: http://www.google.de/apis/maps/documentation/#Geocoding_Structured
|
140
|
+
def query(query)
|
141
|
+
# Check that a Google Maps key has been specified.
|
142
|
+
raise KeyException.new("Cannot use Google geocoder without an API key.") if @api_key.nil?
|
143
|
+
|
144
|
+
# Compute the URL to send a GET query to.
|
145
|
+
url = URI.escape(GET_URL % [ query, 'json', @api_key.to_s ])
|
146
|
+
|
147
|
+
# Perform the query via HTTP.
|
148
|
+
response =
|
149
|
+
begin
|
150
|
+
Net::HTTP.get_response(URI.parse(url))
|
151
|
+
rescue Exception => e
|
152
|
+
raise ConnectionException.new("Could not connect to '#{url}': #{e.message}")
|
153
|
+
end
|
154
|
+
body = response.body
|
155
|
+
|
156
|
+
# Parse the response JSON.
|
157
|
+
result = JSON.parse(body)
|
158
|
+
|
159
|
+
@last_status_code = result['Status']['code']
|
160
|
+
|
161
|
+
# Check that the query was successful.
|
162
|
+
if @last_status_code == G_GEO_BAD_KEY then
|
163
|
+
raise KeyException.new("Invalid API key: '#{@api_key}'.")
|
164
|
+
elsif @last_status_code == G_GEO_SERVER_ERROR then
|
165
|
+
raise ConnectionException.new("There was an error when connecting to the server. Result code was: #{status}.")
|
166
|
+
elsif @last_status_code == G_GEO_UNKNOWN_ADDRESS then
|
167
|
+
return nil
|
168
|
+
end
|
169
|
+
|
170
|
+
return result
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#--
|
2
|
+
# Project: google_checkout4r
|
3
|
+
# File: test/checkout/integration/checkout_command.rb
|
4
|
+
# Author: Manuel Holtgrewe <purestorm at ggnore dot net>
|
5
|
+
# Copyright: (c) 2007 by Manuel Holtgrewe
|
6
|
+
# License: MIT License as follows:
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# "Software"), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to permit
|
13
|
+
# persons to whom the Software is furnished to do so, subject to the
|
14
|
+
# following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be included
|
17
|
+
# in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
20
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
25
|
+
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../../test_helper'
|
29
|
+
|
30
|
+
require 'test/checkout/frontend_configuration'
|
31
|
+
|
32
|
+
require 'google4r/checkout'
|
33
|
+
|
34
|
+
# Integration tests for the CheckoutCommand class.
|
35
|
+
#
|
36
|
+
# Tests the CheckoutCommand class against the Google Checkout Web Service.
|
37
|
+
class Google4R::Checkout::CheckoutCommandIntegrationTest < Test::Unit::TestCase
|
38
|
+
include Google4R::Checkout
|
39
|
+
|
40
|
+
def setup
|
41
|
+
@frontend = Frontend.new(FRONTEND_CONFIGURATION)
|
42
|
+
@frontend.tax_table_factory = TestTaxTableFactory.new
|
43
|
+
@command = @frontend.create_checkout_command
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_sending_to_google_works_with_valid_request
|
47
|
+
setup_command(@command)
|
48
|
+
result = @command.send_to_google_checkout
|
49
|
+
assert_kind_of CheckoutRedirectResponse, result
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_using_invalid_credentials_raise_google_checkout_error
|
53
|
+
invalid_patches = [ [ :merchant_id, 'invalid' ], [ :merchant_key, 'invalid' ] ]
|
54
|
+
|
55
|
+
invalid_patches.each do |patch|
|
56
|
+
config = FRONTEND_CONFIGURATION.dup
|
57
|
+
config[patch[0]] = patch[1]
|
58
|
+
@frontend = Frontend.new(config)
|
59
|
+
@frontend.tax_table_factory = TestTaxTableFactory.new
|
60
|
+
@command = @frontend.create_checkout_command
|
61
|
+
|
62
|
+
setup_command(@command)
|
63
|
+
assert_raises(GoogleCheckoutError) { @command.send_to_google_checkout }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_invalid_xml_raises_google_checkout_error
|
68
|
+
class << @command
|
69
|
+
def to_xml
|
70
|
+
''
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
setup_command(@command)
|
75
|
+
assert_raises(GoogleCheckoutError) { @command.send_to_google_checkout }
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
# Sets up the given CheckoutCommand so it contains some
|
81
|
+
# shipping methods and its cart contains some items.
|
82
|
+
def setup_command(command)
|
83
|
+
# Add shipping methods.
|
84
|
+
command.create_shipping_method(FlatRateShipping) do |shipping|
|
85
|
+
shipping.name = 'UPS Ground Shipping'
|
86
|
+
shipping.price = Money.new(2000) # USD 20
|
87
|
+
shipping.create_allowed_area(UsCountryArea) do |area|
|
88
|
+
area.area = UsCountryArea::ALL
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add items to the cart.
|
93
|
+
1.upto(5) do |i|
|
94
|
+
command.cart.create_item do |item|
|
95
|
+
item.name = "Test Item #{i}"
|
96
|
+
item.description = "This is a test item (#{i})"
|
97
|
+
item.unit_price = Money.new(350)
|
98
|
+
item.quantity = i * 3
|
99
|
+
item.id = "test-#{i}-123456789"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|