Class: GoogleCheckout::Cart
- Includes:
- GoogleCheckout
- Defined in:
- lib/google-checkout/cart.rb
Overview
This class represents a cart for Google Checkout. After initializing it with a merchant_id
and merchant_key
, you add items via add_item, and can then get xml via to_xml, or html code for a form that provides a checkout button via checkout_button.
Example:
item = {
:name => 'A Quarter',
:description => 'One shiny quarter.',
:price => 0.25
}
@cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item)
@cart.add_item(:name => "Pancakes",
:description => "Flapjacks by mail."
:price => 0.50,
:quantity => 10,
"merchant-item-id" => '2938292839')
Then in your view:
Checkout here! <%= @cart.checkout_button %>
This object is also useful for getting back a url to the image for a Google Checkout button. You can use this image in forms that submit back to your own server for further processing via Google Checkout’s level 2 XML API.
Constant Summary collapse
- SANDBOX_CHECKOUT_URL =
"https://sandbox.google.com/checkout/cws/v2/Merchant/%s/checkout"
- PRODUCTION_CHECKOUT_URL =
"https://checkout.google.com/cws/v2/Merchant/%s/checkout"
- DefaultButtonOpts =
The default options for drawing in the button that are filled in when checkout_button or button_url is called.
{ :size => :medium, :style => 'white', :variant => 'text', :loc => 'en_US', :buy_or_checkout => nil, }
Constants included from GoogleCheckout
Constants inherited from Command
GoogleCheckout::Command::PRODUCTION_REQUEST_URL, GoogleCheckout::Command::SANDBOX_REQUEST_URL
Instance Attribute Summary collapse
-
#continue_shopping_url ⇒ Object
Returns the value of attribute continue_shopping_url.
-
#edit_cart_url ⇒ Object
You can provide extra data that will be sent to Google and returned with the NewOrderNotification.
-
#merchant_calculations_url ⇒ Object
Returns the value of attribute merchant_calculations_url.
- #parameterized_urls ⇒ Object
- #shipping_methods ⇒ Object
Attributes inherited from Command
#currency, #merchant_id, #merchant_key
Instance Method Summary collapse
-
#add_item(item) ⇒ Object
This method puts items in the cart.
-
#button_url(opts = {}) ⇒ Object
Given a set of options for the button, button_url returns the URL for the button image.
-
#checkout_button(button_opts = {}) ⇒ Object
Returns HTML for a checkout form for buying all the items in the cart.
- #contents ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(merchant_id, merchant_key, *items) ⇒ Cart
constructor
You need to supply, as strings, the
merchant_id
andmerchant_key
used to identify your store to Google. - #merchant_private_data ⇒ Object
- #merchant_private_data=(value) ⇒ Object
-
#signature ⇒ Object
Returns the signature for the cart XML.
-
#size ⇒ Object
Number of items in the cart.
- #submit_domain ⇒ Object
-
#submit_url ⇒ Object
The Google Checkout form submission url.
-
#to_xml ⇒ Object
This is the important method; it generatest the XML call.
Methods included from GoogleCheckout
production?, sandbox?, use_production, use_sandbox
Methods inherited from Command
Constructor Details
#initialize(merchant_id, merchant_key, *items) ⇒ Cart
You need to supply, as strings, the merchant_id
and merchant_key
used to identify your store to Google. You may optionally supply one or more items to put inside the cart.
86 87 88 89 |
# File 'lib/google-checkout/cart.rb', line 86 def initialize(merchant_id, merchant_key, *items) super(merchant_id, merchant_key) items.each { |i| add_item i } end |
Instance Attribute Details
#continue_shopping_url ⇒ Object
Returns the value of attribute continue_shopping_url.
66 67 68 |
# File 'lib/google-checkout/cart.rb', line 66 def continue_shopping_url @continue_shopping_url end |
#edit_cart_url ⇒ Object
You can provide extra data that will be sent to Google and returned with the NewOrderNotification.
This should be a Hash and will be turned into XML with proper escapes.
Beware using symbols as values. They may be set as sub-keys instead of values, so use a String or other datatype.
65 66 67 |
# File 'lib/google-checkout/cart.rb', line 65 def edit_cart_url @edit_cart_url end |
#merchant_calculations_url ⇒ Object
Returns the value of attribute merchant_calculations_url.
68 69 70 |
# File 'lib/google-checkout/cart.rb', line 68 def merchant_calculations_url @merchant_calculations_url end |
#parameterized_urls ⇒ Object
115 116 117 |
# File 'lib/google-checkout/cart.rb', line 115 def parameterized_urls @parameterized_urls ||= [] end |
#shipping_methods ⇒ Object
119 120 121 |
# File 'lib/google-checkout/cart.rb', line 119 def shipping_methods @shipping_methods ||= [] end |
Instance Method Details
#add_item(item) ⇒ Object
This method puts items in the cart. item
may be a hash, or have a method named to_google_product
that returns a hash with the required values.
-
name
-
description (a brief description as it will appear on the bill)
-
price
You may fill in some optional values as well:
-
quantity (defaults to 1)
-
currency (defaults to ‘USD’)
-
subscription
** type (defaults to ‘google’) ** period (defaults to ‘MONTHLY’) ** start_date ** no_charge_after ** payment_times ** max_charge ** name ** description ** price ** quantity
-
digital_content
** disposition ** description
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/google-checkout/cart.rb', line 165 def add_item(item) @xml = nil if item.respond_to? :to_google_product item = item.to_google_product end # We need to check that the necessary keys are in the hash, # Otherwise the error will happen in the middle of to_xml, # and the bug will be harder to track. missing_keys = [ :name, :description, :price ].select { |key| !item.include? key } unless missing_keys.empty? raise ArgumentError, "Required keys missing: #{missing_keys.inspect}" end contents << { :quantity => 1, :currency => 'USD' }.merge(item) item end |
#button_url(opts = {}) ⇒ Object
Given a set of options for the button, button_url returns the URL for the button image. The options are the same as those specified on checkout.google.com/seller/checkout_buttons.html , with a couple of extra options for convenience. Rather than specifying the width and height manually, you may specify :size to be one of :small, :medium, or :large, and that you may set :buy_or_checkout to :buy_now or :checkout to get a ‘Buy Now’ button versus a ‘Checkout’ button. If you don’t specify :buy_or_checkout, the Cart will try to guess based on if the cart has more than one item in it. Whatever you don’t pass will be filled in with the defaults from DefaultButtonOpts.
http://checkout.google.com//checkout.gif
http://sandbox.google.com/checkout//checkout.gif
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/google-checkout/cart.rb', line 380 def (opts = {}) opts = DefaultButtonOpts.merge opts opts[:buy_or_checkout] ||= contents.size > 1 ? :checkout : :buy_now opts.merge! ButtonSizes[opts[:buy_or_checkout]][opts[:size]] bname = opts[:buy_or_checkout] == :buy_now ? 'buy.gif' : 'checkout.gif' opts.delete :size opts.delete :buy_or_checkout opts[:merchant_id] = @merchant_id path = opts.map { |k,v| "#{k}=#{v}" }.join('&') # HACK Sandbox graphics are in the checkout subdirectory subdir = "" if GoogleCheckout.sandbox? && (bname == "checkout.gif" || bname == "buy.gif") subdir = "checkout/" end protocol = opts[:https] ? 'https' : 'http' # TODO Use /checkout/buttons/checkout.gif if in sandbox. "#{protocol}://#{submit_domain}/#{ subdir }buttons/#{bname}?#{path}" end |
#checkout_button(button_opts = {}) ⇒ Object
Returns HTML for a checkout form for buying all the items in the cart.
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/google-checkout/cart.rb', line 335 def ( = {}) @xml or to_xml burl = () html = Builder::XmlMarkup.new(:indent => 2) html.form({ :action => submit_url, :style => 'border: 0;', :id => 'BB_BuyButtonForm', :method => 'post', :name => 'BB_BuyButtonForm' }) do html.input({ :name => 'cart', :type => 'hidden', :value => Base64.encode64(@xml).gsub("\n", '') }) html.input({ :name => 'signature', :type => 'hidden', :value => Base64.encode64(signature).gsub("\n", '') }) html.input({ :alt => 'Google Checkout', :style => "width: auto;", :src => (), :type => 'image' }) end end |
#contents ⇒ Object
95 96 97 |
# File 'lib/google-checkout/cart.rb', line 95 def contents @contents ||= [] end |
#empty? ⇒ Boolean
91 92 93 |
# File 'lib/google-checkout/cart.rb', line 91 def empty? contents.empty? end |
#merchant_private_data ⇒ Object
99 100 101 |
# File 'lib/google-checkout/cart.rb', line 99 def merchant_private_data @merchant_private_data ||= '' end |
#merchant_private_data=(value) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/google-checkout/cart.rb', line 103 def merchant_private_data=(value) if value.is_a?(Hash) xml = Builder::XmlMarkup.new value.each do |key, value| xml.tag!(key, value) end @merchant_private_data = xml.target! elsif value.is_a?(String) @merchant_private_data = value end end |
#signature ⇒ Object
Returns the signature for the cart XML.
327 328 329 330 331 |
# File 'lib/google-checkout/cart.rb', line 327 def signature @xml or to_xml digest = OpenSSL::Digest::Digest.new('sha1') OpenSSL::HMAC.digest(digest, @merchant_key, @xml) end |
#size ⇒ Object
Number of items in the cart.
124 125 126 |
# File 'lib/google-checkout/cart.rb', line 124 def size contents.size end |
#submit_domain ⇒ Object
128 129 130 |
# File 'lib/google-checkout/cart.rb', line 128 def submit_domain (GoogleCheckout.production? ? 'checkout' : 'sandbox') + ".google.com" end |
#submit_url ⇒ Object
The Google Checkout form submission url.
135 136 137 |
# File 'lib/google-checkout/cart.rb', line 135 def submit_url GoogleCheckout.sandbox? ? (SANDBOX_CHECKOUT_URL % @merchant_id) : (PRODUCTION_CHECKOUT_URL % @merchant_id) end |
#to_xml ⇒ Object
This is the important method; it generatest the XML call. It’s fairly lengthy, but trivial. It follows the docs at code.google.com/apis/checkout/developer/index.html#checkout_api
It returns the raw XML string, not encoded.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/google-checkout/cart.rb', line 192 def to_xml raise RuntimeError, "Empty cart" if self.empty? xml = Builder::XmlMarkup.new xml.instruct! @xml = xml.tag!('checkout-shopping-cart', :xmlns => "http://checkout.google.com/schema/2") { xml.tag!("shopping-cart") { xml.items { contents.each { |item| xml.item { if item.key?(:item_id) xml.tag!('merchant-item-id', item[:item_id]) end xml.tag!('item-name') { xml.text! item[:name].to_s } xml.tag!('item-description') { xml.text! item[:description].to_s } xml.tag!('unit-price', :currency => (item[:currency] || 'USD')) { xml.text! item[:price].to_s } xml.quantity { xml.text! item[:quantity].to_s } # subscription item if item.key?(:subscription) sub = item[:subscription] sub_opts = {} sub_opts.merge!(:type => sub[:type].to_s) if sub[:type] sub_opts.merge!(:period => sub[:period].to_s) if sub[:period] sub_opts.merge!(:"start-date" => sub[:start_date].to_s) if sub[:start_date] sub_opts.merge!(:"no-charge-after" => sub[:no_charge_after].to_s) if sub[:no_charge_after] xml.subscription(sub_opts) { xml.payments { if sub.key?(:payment_times) xml.tag!('subscription-payment', :times => sub[:payment_times].to_s) { xml.tag!('maximum-charge', :currency => (item[:currency] || 'USD')) { xml.text! sub[:max_charge].to_s } } else xml.tag!('subscription-payment') { xml.tag!('maximum-charge', :currency => (item[:currency] || 'USD')) { xml.text! sub[:max_charge].to_s } } end } xml.tag!('recurrent-item') { xml.tag!('item-name') { xml.text! sub[:name].to_s } xml.tag!('item-description') { xml.text! sub[:description].to_s } xml.tag!('unit-price', :currency => (item[:currency] || 'USD')) { xml.text! sub[:price].to_s } xml.quantity { xml.text! sub[:quantity].to_s } } } end # digital delivery if item.key?(:digital_content) content = item[:digital_content] xml.tag!('digital-content') { xml.tag!('display-disposition') { xml.text! content[:disposition] } xml.description { xml.text! content[:description].to_s } if content[:description] } end xml.tag!('merchant-private-item-data') { xml << item[:merchant_private_item_data] } if item.key?(:merchant_private_item_data) } } } unless merchant_private_data.empty? xml.tag!("merchant-private-data") { xml << merchant_private_data } end } xml.tag!('checkout-flow-support') { xml.tag!('merchant-checkout-flow-support') { xml.tag!('edit-cart-url', @edit_cart_url) if @edit_cart_url xml.tag!('continue-shopping-url', @continue_shopping_url) if @continue_shopping_url xml.tag!("request-buyer-phone-number", false) xml.tag!('merchant-calculations') { xml.tag!('merchant-calculations-url') { xml.text! @merchant_calculations_url } } if @merchant_calculations_url # TODO tax-tables xml.tag!('parameterized-urls') { parameterized_urls.each do |param_url| xml.tag!('parameterized-url', :url => param_url[:url]) { next if param_url[:parameters].nil? next if param_url[:parameters].empty? xml.tag!('parameters') { param_url[:parameters].each do |parameter| xml.tag!('url-parameter', :name => parameter[:name], :type => parameter[:type]) {} end } } end } unless parameterized_urls.empty? xml.tag!('shipping-methods') { shipping_methods.each do |shipping_method| xml << shipping_method.to_xml end } } } } @xml.dup end |