google4r 0.0.1
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.
- 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
|