vizjerai-google-checkout 0.0.5
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/History.txt +14 -0
- data/MIT-LICENSE.txt +23 -0
- data/README.txt +143 -0
- data/Rakefile +34 -0
- data/VERSION.yml +4 -0
- data/examples/google_notifications_controller.rb +159 -0
- data/lib/duck_punches/hpricot.rb +24 -0
- data/lib/google-checkout/cart.rb +304 -0
- data/lib/google-checkout/command.rb +255 -0
- data/lib/google-checkout/geography/area.rb +11 -0
- data/lib/google-checkout/geography/postal.rb +26 -0
- data/lib/google-checkout/geography/us_country.rb +24 -0
- data/lib/google-checkout/geography/us_state.rb +22 -0
- data/lib/google-checkout/geography/us_zip.rb +22 -0
- data/lib/google-checkout/geography/world.rb +12 -0
- data/lib/google-checkout/geography.rb +7 -0
- data/lib/google-checkout/merchant_calculation.rb +30 -0
- data/lib/google-checkout/notification.rb +352 -0
- data/lib/google-checkout/shipping/filters.rb +32 -0
- data/lib/google-checkout/shipping/flat_rate.rb +26 -0
- data/lib/google-checkout/shipping/merchant_calculated.rb +29 -0
- data/lib/google-checkout/shipping/method.rb +11 -0
- data/lib/google-checkout/shipping/pickup.rb +22 -0
- data/lib/google-checkout/shipping/restrictions.rb +32 -0
- data/lib/google-checkout/shipping.rb +8 -0
- data/lib/google-checkout.rb +65 -0
- data/spec/fixtures/google/checkout-shopping-cart.xml +22 -0
- data/spec/fixtures/google/commands/add-merchant-order-number.xml +5 -0
- data/spec/fixtures/google/commands/add-tracking-data.xml +8 -0
- data/spec/fixtures/google/commands/archive-order.xml +3 -0
- data/spec/fixtures/google/commands/authorize-order.xml +2 -0
- data/spec/fixtures/google/commands/cancel-order.xml +5 -0
- data/spec/fixtures/google/commands/charge-order.xml +4 -0
- data/spec/fixtures/google/commands/deliver-order.xml +9 -0
- data/spec/fixtures/google/commands/process-order.xml +2 -0
- data/spec/fixtures/google/commands/refund-order.xml +6 -0
- data/spec/fixtures/google/commands/send-buyer-message.xml +7 -0
- data/spec/fixtures/google/commands/unarchive-order.xml +2 -0
- data/spec/fixtures/google/merchant_calculations/shipping.xml +40 -0
- data/spec/fixtures/google/notifications/authorization-amount-notification.xml +10 -0
- data/spec/fixtures/google/notifications/charge-amount-notification.xml +8 -0
- data/spec/fixtures/google/notifications/chargeback-amount-notification.xml +8 -0
- data/spec/fixtures/google/notifications/new-order-notification.xml +94 -0
- data/spec/fixtures/google/notifications/order-state-change-notification.xml +11 -0
- data/spec/fixtures/google/notifications/refund-amount-notification.xml +8 -0
- data/spec/fixtures/google/notifications/risk-information-notification.xml +23 -0
- data/spec/fixtures/google/responses/checkout-redirect.xml +5 -0
- data/spec/fixtures/google/responses/error-charged.xml +5 -0
- data/spec/fixtures/google/responses/error.xml +5 -0
- data/spec/fixtures/google/responses/request-received.xml +3 -0
- data/spec/google-checkout/cart_spec.rb +110 -0
- data/spec/google-checkout/command_spec.rb +216 -0
- data/spec/google-checkout/geography/postal_spec.rb +26 -0
- data/spec/google-checkout/geography/us_country_spec.rb +26 -0
- data/spec/google-checkout/geography/us_state_spec.rb +11 -0
- data/spec/google-checkout/geography/us_zip_spec.rb +11 -0
- data/spec/google-checkout/geography/world_spec.rb +12 -0
- data/spec/google-checkout/merchant_calculation_spec.rb +17 -0
- data/spec/google-checkout/notification_spec.rb +253 -0
- data/spec/google-checkout/response_spec.rb +49 -0
- data/spec/google-checkout/shipping/flat_rate_spec.rb +46 -0
- data/spec/google-checkout/shipping/merchant_calculated_spec.rb +70 -0
- data/spec/google-checkout/shipping/pickup_spec.rb +22 -0
- data/spec/google-checkout_spec.rb +15 -0
- data/spec/spec_helper.rb +47 -0
- data/support/cacert.pem +7815 -0
- metadata +145 -0
@@ -0,0 +1,304 @@
|
|
1
|
+
|
2
|
+
module GoogleCheckout
|
3
|
+
|
4
|
+
# These are the only sizes allowed by Google. These shouldn't be needed
|
5
|
+
# by most people; just specify the :size and :buy_or_checkout options to
|
6
|
+
# Cart#checkout_button and the sizes are filled in automatically.
|
7
|
+
ButtonSizes = {
|
8
|
+
:checkout => {
|
9
|
+
:small => { :w => 160, :h => 43 },
|
10
|
+
:medium => { :w => 168, :h => 44 },
|
11
|
+
:large => { :w => 180, :h => 46 },
|
12
|
+
},
|
13
|
+
|
14
|
+
:buy_now => {
|
15
|
+
:small => { :w => 117, :h => 48 },
|
16
|
+
:medium => { :w => 121, :h => 44 },
|
17
|
+
:large => { :w => 121, :h => 44 },
|
18
|
+
},
|
19
|
+
}
|
20
|
+
|
21
|
+
##
|
22
|
+
# This class represents a cart for Google Checkout. After initializing it
|
23
|
+
# with a +merchant_id+ and +merchant_key+, you add items via add_item,
|
24
|
+
# and can then get xml via to_xml, or html code for a form that
|
25
|
+
# provides a checkout button via checkout_button.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# item = {
|
30
|
+
# :name => 'A Quarter',
|
31
|
+
# :description => 'One shiny quarter.',
|
32
|
+
# :price => 0.25
|
33
|
+
# }
|
34
|
+
# @cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item)
|
35
|
+
# @cart.add_item(:name => "Pancakes",
|
36
|
+
# :description => "Flapjacks by mail."
|
37
|
+
# :price => 0.50,
|
38
|
+
# :quantity => 10,
|
39
|
+
# "merchant-item-id" => '2938292839')
|
40
|
+
#
|
41
|
+
# Then in your view:
|
42
|
+
#
|
43
|
+
# Checkout here! <%= @cart.checkout_button %>
|
44
|
+
#
|
45
|
+
# This object is also useful for getting back a url to the image for a Google
|
46
|
+
# Checkout button. You can use this image in forms that submit back to your own
|
47
|
+
# server for further processing via Google Checkout's level 2 XML API.
|
48
|
+
|
49
|
+
class Cart < Command
|
50
|
+
|
51
|
+
include GoogleCheckout
|
52
|
+
|
53
|
+
SANDBOX_CHECKOUT_URL = "https://sandbox.google.com/checkout/cws/v2/Merchant/%s/checkout"
|
54
|
+
PRODUCTION_CHECKOUT_URL = "https://checkout.google.com/cws/v2/Merchant/%s/checkout"
|
55
|
+
|
56
|
+
##
|
57
|
+
# You can provide extra data that will be sent to Google and returned with
|
58
|
+
# the NewOrderNotification.
|
59
|
+
#
|
60
|
+
# This should be a Hash and will be turned into XML with proper escapes.
|
61
|
+
#
|
62
|
+
# Beware using symbols as values. They may be set as sub-keys instead of values,
|
63
|
+
# so use a String or other datatype.
|
64
|
+
|
65
|
+
attr_accessor :merchant_private_data
|
66
|
+
|
67
|
+
attr_accessor :edit_cart_url
|
68
|
+
attr_accessor :continue_shopping_url
|
69
|
+
|
70
|
+
attr_accessor :merchant_calculations_url
|
71
|
+
|
72
|
+
attr_accessor :shipping_methods
|
73
|
+
|
74
|
+
# The default options for drawing in the button that are filled in when
|
75
|
+
# checkout_button or button_url is called.
|
76
|
+
DefaultButtonOpts = {
|
77
|
+
:size => :medium,
|
78
|
+
:style => 'white',
|
79
|
+
:variant => 'text',
|
80
|
+
:loc => 'en_US',
|
81
|
+
:buy_or_checkout => nil,
|
82
|
+
}
|
83
|
+
|
84
|
+
# You need to supply, as strings, the +merchant_id+ and +merchant_key+
|
85
|
+
# used to identify your store to Google. You may optionally supply one
|
86
|
+
# or more items to put inside the cart.
|
87
|
+
def initialize(merchant_id, merchant_key, *items)
|
88
|
+
super(merchant_id, merchant_key)
|
89
|
+
@contents = []
|
90
|
+
@merchant_private_data = {}
|
91
|
+
@shipping_methods = []
|
92
|
+
items.each { |i| add_item i }
|
93
|
+
end
|
94
|
+
|
95
|
+
def empty?
|
96
|
+
@contents.empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
# Number of items in the cart.
|
100
|
+
def size
|
101
|
+
@contents.size
|
102
|
+
end
|
103
|
+
|
104
|
+
def submit_domain
|
105
|
+
(GoogleCheckout.production? ? 'checkout' : 'sandbox') + ".google.com"
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# The Google Checkout form submission url.
|
110
|
+
|
111
|
+
def submit_url
|
112
|
+
GoogleCheckout.sandbox? ? (SANDBOX_CHECKOUT_URL % @merchant_id) : (PRODUCTION_CHECKOUT_URL % @merchant_id)
|
113
|
+
end
|
114
|
+
|
115
|
+
# This method puts items in the cart.
|
116
|
+
# +item+ may be a hash, or have a method named +to_google_product+ that
|
117
|
+
# returns a hash with the required values.
|
118
|
+
# * name
|
119
|
+
# * description (a brief description as it will appear on the bill)
|
120
|
+
# * price
|
121
|
+
# You may fill in some optional values as well:
|
122
|
+
# * quantity (defaults to 1)
|
123
|
+
# * currency (defaults to 'USD')
|
124
|
+
def add_item(item)
|
125
|
+
@xml = nil
|
126
|
+
if item.respond_to? :to_google_product
|
127
|
+
item = item.to_google_product
|
128
|
+
end
|
129
|
+
|
130
|
+
# We need to check that the necessary keys are in the hash,
|
131
|
+
# Otherwise the error will happen in the middle of to_xml,
|
132
|
+
# and the bug will be harder to track.
|
133
|
+
missing_keys = [ :name, :description, :price ].select { |key|
|
134
|
+
!item.include? key
|
135
|
+
}
|
136
|
+
|
137
|
+
unless missing_keys.empty?
|
138
|
+
raise ArgumentError,
|
139
|
+
"Required keys missing: #{missing_keys.inspect}"
|
140
|
+
end
|
141
|
+
|
142
|
+
@contents << { :quantity => 1, :currency => 'USD' }.merge(item)
|
143
|
+
item
|
144
|
+
end
|
145
|
+
|
146
|
+
# This is the important method; it generatest the XML call.
|
147
|
+
# It's fairly lengthy, but trivial. It follows the docs at
|
148
|
+
# http://code.google.com/apis/checkout/developer/index.html#checkout_api
|
149
|
+
#
|
150
|
+
# It returns the raw XML string, not encoded.
|
151
|
+
def to_xml
|
152
|
+
raise RuntimeError, "Empty cart" if self.empty?
|
153
|
+
|
154
|
+
xml = Builder::XmlMarkup.new
|
155
|
+
xml.instruct!
|
156
|
+
@xml = xml.tag!('checkout-shopping-cart', :xmlns => "http://checkout.google.com/schema/2") {
|
157
|
+
xml.tag!("shopping-cart") {
|
158
|
+
xml.items {
|
159
|
+
@contents.each { |item|
|
160
|
+
xml.item {
|
161
|
+
if item.key?(:item_id)
|
162
|
+
xml.tag!('merchant-item-id', item[:item_id])
|
163
|
+
end
|
164
|
+
xml.tag!('item-name') {
|
165
|
+
xml.text! item[:name].to_s
|
166
|
+
}
|
167
|
+
xml.tag!('item-description') {
|
168
|
+
xml.text! item[:description].to_s
|
169
|
+
}
|
170
|
+
xml.tag!('unit-price', :currency => (item[:currency] || 'USD')) {
|
171
|
+
xml.text! item[:price].to_s
|
172
|
+
}
|
173
|
+
xml.quantity {
|
174
|
+
xml.text! item[:quantity].to_s
|
175
|
+
}
|
176
|
+
xml.tag!('merchant-private-item-data') {
|
177
|
+
xml << item[:merchant_private_item_data]
|
178
|
+
} if item.key?(:merchant_private_item_data)
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
unless @merchant_private_data.empty?
|
183
|
+
xml.tag!("merchant-private-data") {
|
184
|
+
@merchant_private_data.each do |key, value|
|
185
|
+
xml.tag!(key, value)
|
186
|
+
end
|
187
|
+
}
|
188
|
+
end
|
189
|
+
}
|
190
|
+
xml.tag!('checkout-flow-support') {
|
191
|
+
xml.tag!('merchant-checkout-flow-support') {
|
192
|
+
xml.tag!('edit-cart-url', @edit_cart_url) if @edit_cart_url
|
193
|
+
xml.tag!('continue-shopping-url', @continue_shopping_url) if @continue_shopping_url
|
194
|
+
xml.tag!("request-buyer-phone-number", false)
|
195
|
+
xml.tag!('merchant-calculations') {
|
196
|
+
xml.tag!('merchant-calculations-url') {
|
197
|
+
xml.text! @merchant_calculations_url
|
198
|
+
}
|
199
|
+
} if @merchant_calculations_url
|
200
|
+
|
201
|
+
# TODO tax-tables
|
202
|
+
xml.tag!("tax-tables") {
|
203
|
+
xml.tag!("default-tax-table") {
|
204
|
+
xml.tag!("tax-rules") {
|
205
|
+
xml.tag!("default-tax-rule") {
|
206
|
+
xml.tag!("shipping-taxed", false)
|
207
|
+
xml.tag!("rate", "0.00")
|
208
|
+
xml.tag!("tax-area") {
|
209
|
+
xml.tag!("world-area")
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
xml.tag!('shipping-methods') {
|
217
|
+
@shipping_methods.each do |shipping_method|
|
218
|
+
xml << shipping_method.to_xml
|
219
|
+
end
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
@xml.dup
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns the signature for the cart XML.
|
228
|
+
def signature
|
229
|
+
@xml or to_xml
|
230
|
+
HMAC::SHA1.digest(@merchant_key, @xml)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Returns HTML for a checkout form for buying all the items in the
|
234
|
+
# cart.
|
235
|
+
def checkout_button(button_opts = {})
|
236
|
+
@xml or to_xml
|
237
|
+
burl = button_url(button_opts)
|
238
|
+
html = Builder::XmlMarkup.new(:indent => 2)
|
239
|
+
html.form({
|
240
|
+
:action => submit_url,
|
241
|
+
:style => 'border: 0;',
|
242
|
+
:id => 'BB_BuyButtonForm',
|
243
|
+
:method => 'post',
|
244
|
+
:name => 'BB_BuyButtonForm'
|
245
|
+
}) do
|
246
|
+
html.input({
|
247
|
+
:name => 'cart',
|
248
|
+
:type => 'hidden',
|
249
|
+
:value => Base64.encode64(@xml).gsub("\n", '')
|
250
|
+
})
|
251
|
+
html.input({
|
252
|
+
:name => 'signature',
|
253
|
+
:type => 'hidden',
|
254
|
+
:value => Base64.encode64(signature).gsub("\n", '')
|
255
|
+
})
|
256
|
+
html.input({
|
257
|
+
:alt => 'Google Checkout',
|
258
|
+
:style => "width: auto;",
|
259
|
+
:src => button_url(button_opts),
|
260
|
+
:type => 'image'
|
261
|
+
})
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Given a set of options for the button, button_url returns the URL
|
266
|
+
# for the button image.
|
267
|
+
# The options are the same as those specified on
|
268
|
+
# http://checkout.google.com/seller/checkout_buttons.html , with a
|
269
|
+
# couple of extra options for convenience. Rather than specifying the
|
270
|
+
# width and height manually, you may specify :size to be one of :small,
|
271
|
+
# :medium, or :large, and that you may set :buy_or_checkout to :buy_now
|
272
|
+
# or :checkout to get a 'Buy Now' button versus a 'Checkout' button. If
|
273
|
+
# you don't specify :buy_or_checkout, the Cart will try to guess based
|
274
|
+
# on if the cart has more than one item in it. Whatever you don't pass
|
275
|
+
# will be filled in with the defaults from DefaultButtonOpts.
|
276
|
+
#
|
277
|
+
# http://checkout.google.com/buttons/checkout.gif
|
278
|
+
# http://sandbox.google.com/checkout/buttons/checkout.gif
|
279
|
+
|
280
|
+
def button_url(opts = {})
|
281
|
+
opts = DefaultButtonOpts.merge opts
|
282
|
+
opts[:buy_or_checkout] ||= @contents.size > 1 ? :checkout : :buy_now
|
283
|
+
opts.merge! ButtonSizes[opts[:buy_or_checkout]][opts[:size]]
|
284
|
+
bname = opts[:buy_or_checkout] == :buy_now ? 'buy.gif' : 'checkout.gif'
|
285
|
+
opts.delete :size
|
286
|
+
opts.delete :buy_or_checkout
|
287
|
+
opts[:merchant_id] = @merchant_id
|
288
|
+
|
289
|
+
path = opts.map { |k,v| "#{k}=#{v}" }.join('&')
|
290
|
+
|
291
|
+
# HACK Sandbox graphics are in the checkout subdirectory
|
292
|
+
subdir = ""
|
293
|
+
if GoogleCheckout.sandbox? && bname == "checkout.gif"
|
294
|
+
subdir = "checkout/"
|
295
|
+
end
|
296
|
+
|
297
|
+
protocol = opts[:https] ? 'https' : 'http'
|
298
|
+
|
299
|
+
# TODO Use /checkout/buttons/checkout.gif if in sandbox.
|
300
|
+
"#{protocol}://#{submit_domain}/#{ subdir }buttons/#{bname}?#{path}"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
|
2
|
+
# TODO
|
3
|
+
#
|
4
|
+
# * Use standard ssl certs
|
5
|
+
|
6
|
+
module GoogleCheckout
|
7
|
+
|
8
|
+
##
|
9
|
+
# Abstract class for commands.
|
10
|
+
#
|
11
|
+
# https://sandbox.google.com/checkout/cws/v2/Merchant/1234567890/request
|
12
|
+
# https://checkout.google.com/cws/v2/Merchant/1234567890/request
|
13
|
+
|
14
|
+
class Command
|
15
|
+
|
16
|
+
attr_accessor :merchant_id, :merchant_key, :currency
|
17
|
+
|
18
|
+
SANDBOX_REQUEST_URL = "https://sandbox.google.com/checkout/cws/v2/Merchant/%s/request"
|
19
|
+
PRODUCTION_REQUEST_URL = "https://checkout.google.com/cws/v2/Merchant/%s/request"
|
20
|
+
|
21
|
+
def initialize(merchant_id, merchant_key)
|
22
|
+
@merchant_id = merchant_id
|
23
|
+
@merchant_key = merchant_key
|
24
|
+
|
25
|
+
@currency = "USD"
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Returns the appropriate sandbox or production url for posting API requests.
|
30
|
+
|
31
|
+
def url
|
32
|
+
GoogleCheckout.sandbox? ? (SANDBOX_REQUEST_URL % @merchant_id) : (PRODUCTION_REQUEST_URL % @merchant_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Sends the Command's XML to GoogleCheckout via HTTPS with Basic Auth.
|
37
|
+
#
|
38
|
+
# Returns a GoogleCheckout::RequestReceived or a GoogleCheckout::Error object.
|
39
|
+
|
40
|
+
def post
|
41
|
+
# Create HTTP(S) POST command and set up Basic Authentication.
|
42
|
+
uri = URI.parse(url)
|
43
|
+
|
44
|
+
request = Net::HTTP::Post.new(uri.path)
|
45
|
+
request.basic_auth(@merchant_id, @merchant_key)
|
46
|
+
|
47
|
+
# Set up the HTTP connection object and the SSL layer.
|
48
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
https.use_ssl = true
|
50
|
+
https.cert_store = self.class.x509_store
|
51
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
52
|
+
https.verify_depth = 5
|
53
|
+
|
54
|
+
# Send the request to Google.
|
55
|
+
response = https.request(request, self.to_xml)
|
56
|
+
|
57
|
+
# NOTE Because Notification.parse() is used, the content of objects
|
58
|
+
# will be correctly parsed no matter what the HTTP response code
|
59
|
+
# is from the server.
|
60
|
+
case response
|
61
|
+
when Net::HTTPSuccess, Net::HTTPClientError
|
62
|
+
notification = Notification.parse(response.body)
|
63
|
+
if notification.error? && !notification.message.eql?('You cannot charge an order that is already completely charged')
|
64
|
+
raise APIError, "#{notification.message} [in #{GoogleCheckout.production? ? 'production' : 'sandbox' }]"
|
65
|
+
end
|
66
|
+
return notification
|
67
|
+
when Net::HTTPRedirection, Net::HTTPServerError, Net::HTTPInformation
|
68
|
+
raise "Unexpected response code (#{response.class}): #{response.code} - #{response.message}"
|
69
|
+
else
|
70
|
+
raise "Unknown response code: #{response.code} - #{response.message}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Class method to return the OpenSSL::X509::Store instance for the
|
76
|
+
# CA certificates.
|
77
|
+
#
|
78
|
+
# NOTE May not be thread-safe.
|
79
|
+
|
80
|
+
def self.x509_store
|
81
|
+
return @@x509_store if defined?(@@x509_store)
|
82
|
+
|
83
|
+
cacert_path = File.expand_path(File.dirname(__FILE__) + '/../../support/cacert.pem')
|
84
|
+
|
85
|
+
@@x509_store = OpenSSL::X509::Store.new
|
86
|
+
@@x509_store.add_file(cacert_path)
|
87
|
+
|
88
|
+
return @@x509_store
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Abstract class for all commands associated with an existing order.
|
95
|
+
|
96
|
+
class OrderCommand < Command
|
97
|
+
|
98
|
+
attr_accessor :google_order_number, :amount
|
99
|
+
|
100
|
+
##
|
101
|
+
# Make a new object. Last argument is the Google's order number as received
|
102
|
+
# in the NewOrderNotification.
|
103
|
+
|
104
|
+
def initialize(merchant_id, merchant_key, google_order_number)
|
105
|
+
# TODO raise "Not an order number!" unless google_order_number.is_a? String
|
106
|
+
super(merchant_id, merchant_key)
|
107
|
+
@google_order_number = google_order_number
|
108
|
+
@amount = 0.00
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Create a new ChargeOrder object, set the +amount+, then
|
115
|
+
# +post+ it.
|
116
|
+
|
117
|
+
class ChargeOrder < OrderCommand
|
118
|
+
|
119
|
+
def to_xml
|
120
|
+
raise "Charge amount must be greater than 0!" unless @amount.to_f > 0.0
|
121
|
+
|
122
|
+
xml = Builder::XmlMarkup.new
|
123
|
+
xml.instruct!
|
124
|
+
@xml = xml.tag!('charge-order', {
|
125
|
+
:xmlns => "http://checkout.google.com/schema/2",
|
126
|
+
"google-order-number" => @google_order_number
|
127
|
+
}) do
|
128
|
+
xml.tag!("amount", @amount, {:currency => @currency})
|
129
|
+
end
|
130
|
+
@xml
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Tells Google that the order has shipped.
|
137
|
+
|
138
|
+
class DeliverOrder < OrderCommand
|
139
|
+
|
140
|
+
def to_xml
|
141
|
+
|
142
|
+
xml = Builder::XmlMarkup.new
|
143
|
+
xml.instruct!
|
144
|
+
@xml = xml.tag!('deliver-order', {
|
145
|
+
:xmlns => "http://checkout.google.com/schema/2",
|
146
|
+
"google-order-number" => @google_order_number
|
147
|
+
}) do
|
148
|
+
xml.tag!("send-email", false)
|
149
|
+
end
|
150
|
+
@xml
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Tells Google tracking data for the order.
|
157
|
+
|
158
|
+
class AddTrackingData < OrderCommand
|
159
|
+
|
160
|
+
def initialize(merchant_id, merchant_key, google_order_number, carrier, tracking_number)
|
161
|
+
raise "Unknown carrier. Valid values are DHL, FedEx, UPS, USPS, and Other." unless ["DHL","FedEx","UPS","USPS","Other"].include? carrier
|
162
|
+
|
163
|
+
@carrier = carrier
|
164
|
+
@tracking_number = tracking_number
|
165
|
+
super(merchant_id, merchant_key, google_order_number)
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_xml
|
169
|
+
xml = Builder::XmlMarkup.new
|
170
|
+
xml.instruct!
|
171
|
+
@xml = xml.tag!('add-tracking-data', {
|
172
|
+
:xmlns => "http://checkout.google.com/schema/2",
|
173
|
+
"google-order-number" => @google_order_number
|
174
|
+
}) do
|
175
|
+
xml.tag!('tracking-data') do
|
176
|
+
xml.tag!('carrier', @carrier)
|
177
|
+
xml.tag!('tracking-number', @tracking_number)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
@xml
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Tells Google to refund the order.
|
187
|
+
|
188
|
+
class RefundOrder < OrderCommand
|
189
|
+
|
190
|
+
attr_accessor :reason, :comment
|
191
|
+
|
192
|
+
def initialize(merchant_id, merchant_key, google_order_number)
|
193
|
+
super(merchant_id, merchant_key, google_order_number)
|
194
|
+
@reason = ''
|
195
|
+
@comment = ''
|
196
|
+
end
|
197
|
+
|
198
|
+
def to_xml
|
199
|
+
raise "Refund amount must be greater than 0!" unless @amount.to_f > 0.0
|
200
|
+
raise "Reason must be longer than 0 characters!" unless @reason.length > 0
|
201
|
+
raise "Reason cannot be greater than 140 characters!" if @reason.length > 140
|
202
|
+
raise "Comment cannot be greater than 140 characters!" if @comment.length > 140
|
203
|
+
|
204
|
+
xml = Builder::XmlMarkup.new
|
205
|
+
xml.instruct!
|
206
|
+
@xml = xml.tag!('refund-order', {
|
207
|
+
:xmlns => "http://checkout.google.com/schema/2",
|
208
|
+
"google-order-number" => @google_order_number
|
209
|
+
}) do
|
210
|
+
xml.tag!("amount", @amount, {:currency => @currency})
|
211
|
+
xml.tag!("reason", @reason)
|
212
|
+
xml.tag!("comment", @comment) unless @comment.blank?
|
213
|
+
end
|
214
|
+
@xml
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Send a message to the buyer associated with an order.
|
221
|
+
#
|
222
|
+
# Google will actually send the message to their email address.
|
223
|
+
|
224
|
+
class SendBuyerMessage < OrderCommand
|
225
|
+
|
226
|
+
##
|
227
|
+
# Make a new message to send.
|
228
|
+
#
|
229
|
+
# The last argument is the actual message.
|
230
|
+
#
|
231
|
+
# Call +post+ on the resulting object to submit it to Google for sending.
|
232
|
+
|
233
|
+
def initialize(merchant_id, merchant_key, google_order_number, message)
|
234
|
+
# TODO Raise meaninful error if message is longer than 255 characters
|
235
|
+
raise "Google won't send anything longer than 255 characters! Sorry!" if message.length > 255
|
236
|
+
@message = message
|
237
|
+
super(merchant_id, merchant_key, google_order_number)
|
238
|
+
end
|
239
|
+
|
240
|
+
def to_xml # :nodoc:
|
241
|
+
xml = Builder::XmlMarkup.new
|
242
|
+
xml.instruct!
|
243
|
+
@xml = xml.tag!('send-buyer-message', {
|
244
|
+
:xmlns => "http://checkout.google.com/schema/2",
|
245
|
+
"google-order-number" => @google_order_number
|
246
|
+
}) do
|
247
|
+
xml.tag!("message", @message)
|
248
|
+
xml.tag!("send-email", true)
|
249
|
+
end
|
250
|
+
@xml
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
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,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'
|