google_checkout 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/LICENSE +19 -0
- data/doc/README +108 -0
- data/lib/google-checkout.rb +228 -0
- metadata +46 -0
data/doc/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2006 Peter Elmore (pete.elmore at gmail.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
copy of this software and associated documentation files (the "Software"),
|
5
|
+
to deal in the Software without restriction, including without limitation
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
data/doc/README
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
= GoogleCheckout
|
2
|
+
|
3
|
+
== Installation
|
4
|
+
|
5
|
+
Currently, it's only available as a gem.
|
6
|
+
|
7
|
+
== What is Google Checkout?
|
8
|
+
|
9
|
+
Well, according to Google, "Google Checkout helps you increase sales. And
|
10
|
+
process them for free when you advertise with Google." What it really amounts
|
11
|
+
to is that Google will process your orders for a 10% fee and you get a little
|
12
|
+
shopping cart icon in your ad if you advertise through AdWords. The fee can
|
13
|
+
be paid by running AdWords ads on your site. You can read about it and get an
|
14
|
+
account at http://checkout.google.com/sell .
|
15
|
+
|
16
|
+
== What is google-checkout?
|
17
|
+
|
18
|
+
google-checkout is a module for working with the Google Checkout API
|
19
|
+
(http://code.google.com/apis/checkout/index.html). Specifically, if you have
|
20
|
+
a Google Checkout account, you can use this module to do things like add
|
21
|
+
"Checkout" and "Buy Now" buttons to your site.
|
22
|
+
|
23
|
+
== Brief Example
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require 'google-checkout'
|
27
|
+
|
28
|
+
merchant_id = 'Your merchant id.'
|
29
|
+
merchant_key = 'Your merchant key. Keep this a secret!'
|
30
|
+
|
31
|
+
cart = GoogleCheckout::Cart.new(merchant_id, merchant_key)
|
32
|
+
cart.add_item(:name => 'Chair', :description => 'A sturdy, wooden chair',
|
33
|
+
:price => 44.99)
|
34
|
+
puts cart.checkout_button
|
35
|
+
|
36
|
+
== Usage
|
37
|
+
|
38
|
+
First, you'll need a merchant ID and a merchant key, which you can get from
|
39
|
+
the Google Checkout site once you have an account. After you have that, you
|
40
|
+
can start writing code.
|
41
|
+
|
42
|
+
The class you'll be working with is GoogleCheckout::Cart. Of course, it
|
43
|
+
represents a cart, and you can fill it with items.
|
44
|
+
|
45
|
+
cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item1, item2)
|
46
|
+
cart.add_item item3
|
47
|
+
|
48
|
+
The items you put into the cart should be one of two types:
|
49
|
+
* A Hash containing the following
|
50
|
+
** :name
|
51
|
+
** :description
|
52
|
+
** :price
|
53
|
+
** :quantity (default 1)
|
54
|
+
** :currency (default 'USD')
|
55
|
+
** :regular_shipping, the shipping cost (default $0)
|
56
|
+
* Or any Object that has a method called to_google_product that returns a hash
|
57
|
+
like the one described.
|
58
|
+
|
59
|
+
Once you have a cart full of items, you can generate the XML for the API call
|
60
|
+
by calling Cart#checkout_xml, although you'll probably just want to add a
|
61
|
+
checkout button to your page with Cart#checkout_button. This method generates
|
62
|
+
HTML for a form containing a button and the hidden inputs necessary to call
|
63
|
+
Google Checkout. Cart#checkout_button has plenty of options for controlling
|
64
|
+
the look of the button. Once again, the arguments are passed as a hash,
|
65
|
+
although the defaults are usually reasonable so you might not need to pass
|
66
|
+
anything.
|
67
|
+
|
68
|
+
* :size is the size of the button, one of :small, :medium, or :large. Google
|
69
|
+
is picky about the sizes of these buttons. See GoogleCheckout::ButtonSizes
|
70
|
+
for more information. The default is :medium.
|
71
|
+
* :variant is one of 'disabled' or 'text'. 'disabled' means that the button
|
72
|
+
should be greyed-out; it is used in cases that the item you are selling
|
73
|
+
cannot be bought via Google Checkout. (There's a long list of items that
|
74
|
+
are not allowed at https://checkout.google.com/seller/content_policies.html
|
75
|
+
* :buy_or_checkout must be one of :buy_now or :checkout . This determines the
|
76
|
+
look of the button that will be displayed. The default is to use :checkout
|
77
|
+
if there are two or more items in the cart.
|
78
|
+
* :style must be one of 'white' or 'trans'. 'white' gets you a white button,
|
79
|
+
while 'trans' gets you a transparent button suitable for use on non-white
|
80
|
+
backgrounds. The default is 'white'.
|
81
|
+
|
82
|
+
cart.checkout_button :size => :small, :style => 'trans'
|
83
|
+
|
84
|
+
When users click the button, they will be taken to the Google Checkout page
|
85
|
+
with a cart full of the products you specified, and your work is done.
|
86
|
+
|
87
|
+
== Missing Features
|
88
|
+
|
89
|
+
* Level 1 integration is complete except for tax tables
|
90
|
+
* Level 2 integration is missing.
|
91
|
+
|
92
|
+
See
|
93
|
+
http://checkout.google.com/support/sell/bin/answer.py?answer=42917&topic=8671
|
94
|
+
for more information about the two integration levels.
|
95
|
+
|
96
|
+
If there are missing features I haven't thought of, let me know.
|
97
|
+
|
98
|
+
== Bugs
|
99
|
+
|
100
|
+
No 'hard' bugs, I hope. My contact information is at the bottom of the page
|
101
|
+
if you find one. There may be more subjective bugs (e.g., design issues);
|
102
|
+
feel free to tell me about these, too.
|
103
|
+
|
104
|
+
== Contact Information
|
105
|
+
|
106
|
+
The home page is at http://debu.gs/google-checkout . You can email me at pete
|
107
|
+
dot elmore at gmail dot com. Try to mention Google Checkout in the subject
|
108
|
+
line.
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'hmac-sha1'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
require 'builder/xmlmarkup'
|
7
|
+
|
8
|
+
# For the impatient or forgetful:
|
9
|
+
# item = { :name => 'A Quarter', :description => 'One shiny quarter.',
|
10
|
+
# :price => 0.25 }
|
11
|
+
# cart = GoogleCheckout::Cart.new(merchant_id, merchant_key, item)
|
12
|
+
# cart.add_item(:name => "Pancakes", :description => "Flapjacks by mail."
|
13
|
+
# :price => 0.50, :quantity => 10)
|
14
|
+
# File.open('commerce.html', 'w') { |f|
|
15
|
+
# f.write(
|
16
|
+
# "<html><body>Click this button to buy a quarter and ten pancakes:" +
|
17
|
+
# cart.checkout_button + "</body></html>")
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# If you're interested in a little more detail, you'll want to have a look at
|
21
|
+
# GoogleCheckout::Cart, Cart#add_item, Cart#checkout_xml, and
|
22
|
+
# Cart#checkout_button.
|
23
|
+
module GoogleCheckout
|
24
|
+
CheckoutURL = "checkout.google.com"
|
25
|
+
SubmitURL = "https://#{CheckoutURL}/cws/v2/Merchant"
|
26
|
+
|
27
|
+
|
28
|
+
# These are the only sizes allowed by Google. These shouldn't be needed
|
29
|
+
# by most people; just specify the :size and :buy_or_checkout options to
|
30
|
+
# Cart#checkout_button and the sizes are filled in automatically.
|
31
|
+
ButtonSizes = {
|
32
|
+
:checkout => {
|
33
|
+
:small => { :w => 160, :h => 43 },
|
34
|
+
:medium => { :w => 168, :h => 44 },
|
35
|
+
:large => { :w => 180, :h => 46 },
|
36
|
+
},
|
37
|
+
|
38
|
+
:buy_now => {
|
39
|
+
:small => { :w => 117, :h => 48 },
|
40
|
+
:medium => { :w => 121, :h => 44 },
|
41
|
+
:large => { :w => 121, :h => 44 },
|
42
|
+
},
|
43
|
+
}
|
44
|
+
|
45
|
+
# This class represents a cart for Google Checkout. After initializing it
|
46
|
+
# with a +merchant_id+ and +merchant_key+, you add items via add_item,
|
47
|
+
# and can then get xml via checkout_xml, or html code for a form that
|
48
|
+
# provides a checkout button via checkout_button.
|
49
|
+
class Cart
|
50
|
+
attr_accessor :merchant_id, :merchant_key
|
51
|
+
|
52
|
+
include GoogleCheckout
|
53
|
+
|
54
|
+
# The default options for drawing in the button that are filled in when
|
55
|
+
# checkout_button or button_url is called.
|
56
|
+
DefaultButtonOpts = {
|
57
|
+
:size => :medium,
|
58
|
+
:style => 'white',
|
59
|
+
:variant => 'text',
|
60
|
+
:loc => 'en_US',
|
61
|
+
:buy_or_checkout => nil,
|
62
|
+
}
|
63
|
+
|
64
|
+
# You need to supply, as strings, the +merchant_id+ and +merchant_key+
|
65
|
+
# used to identify your store to Google. You may optionally supply one
|
66
|
+
# or more items to put inside the cart.
|
67
|
+
def initialize(merchant_id, merchant_key, *items)
|
68
|
+
@merchant_id = merchant_id
|
69
|
+
@merchant_key = merchant_key
|
70
|
+
@contents = []
|
71
|
+
items.each { |i| add_item i }
|
72
|
+
end
|
73
|
+
|
74
|
+
def empty?
|
75
|
+
@contents.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Number of items in the cart.
|
79
|
+
def size
|
80
|
+
@contents.size
|
81
|
+
end
|
82
|
+
|
83
|
+
# This method returns the URL to which the form should be submitted.
|
84
|
+
def submit_url
|
85
|
+
[SubmitURL, @merchant_id, 'checkout'].join('/')
|
86
|
+
end
|
87
|
+
|
88
|
+
# This method puts items in the cart.
|
89
|
+
# +item+ may be a hash, or have a method named +to_google_product+ that
|
90
|
+
# returns a hash with the required values.
|
91
|
+
# * name
|
92
|
+
# * description (a brief description as it will appear on the bill)
|
93
|
+
# * price
|
94
|
+
# You may fill in some optional values as well:
|
95
|
+
# * quantity (defaults to 1)
|
96
|
+
# * currency (defaults to 'USD')
|
97
|
+
def add_item(item)
|
98
|
+
@xml = nil
|
99
|
+
if item.respond_to? :to_google_product
|
100
|
+
item = item.to_google_product
|
101
|
+
end
|
102
|
+
|
103
|
+
# We need to check that the necessary keys are in the hash,
|
104
|
+
# Otherwise the error will happen in the middle of checkout_xml,
|
105
|
+
# and the bug will be harder to track.
|
106
|
+
missing_keys = [ :name, :description, :price ].select { |key|
|
107
|
+
!item.include? key
|
108
|
+
}
|
109
|
+
|
110
|
+
unless missing_keys.empty?
|
111
|
+
raise ArgumentError,
|
112
|
+
"Required keys missing: #{missing_keys.inspect}"
|
113
|
+
end
|
114
|
+
|
115
|
+
@contents << { :quantity => 1, :currency => 'USD' }.merge(item)
|
116
|
+
item
|
117
|
+
end
|
118
|
+
|
119
|
+
# This is the important method; it generatest the XML call.
|
120
|
+
# It's fairly lengthy, but trivial. It follows the docs at
|
121
|
+
# http://code.google.com/apis/checkout/developer/index.html#checkout_api
|
122
|
+
#
|
123
|
+
# It returns the raw XML string, not encoded.
|
124
|
+
def checkout_xml
|
125
|
+
raise RuntimeError, "Empty cart" if self.empty?
|
126
|
+
|
127
|
+
xml = Builder::XmlMarkup.new
|
128
|
+
xml.instruct!
|
129
|
+
@xml = xml.tag!('checkout-shopping-cart',
|
130
|
+
:xmlns => "http://checkout.google.com/schema/2") {
|
131
|
+
xml.tag!("shopping-cart") {
|
132
|
+
xml.items {
|
133
|
+
@contents.each { |item|
|
134
|
+
xml.item {
|
135
|
+
xml.tag!('item-name') {
|
136
|
+
xml.text! item[:name].to_s
|
137
|
+
}
|
138
|
+
xml.tag!('item-description') {
|
139
|
+
xml.text! item[:description].to_s
|
140
|
+
}
|
141
|
+
xml.tag!('unit-price',
|
142
|
+
:currency => (item[:currency] || 'USD')) {
|
143
|
+
xml.text! item[:price].to_s
|
144
|
+
}
|
145
|
+
xml.quantity {
|
146
|
+
xml.text! item[:quantity].to_s
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
xml.tag!('checkout-flow-support') {
|
153
|
+
xml.tag!('merchant-checkout-flow-support') {
|
154
|
+
xml.tag!('shipping-methods') {
|
155
|
+
xml.tag!('flat-rate-shipping', :name =>'Shipping') {
|
156
|
+
currency = 'USD'
|
157
|
+
shipping = @contents.inject(0) { |total,i|
|
158
|
+
# Mixing currency not allowed; this library
|
159
|
+
# can't convert between currencies.
|
160
|
+
currency = i[:currency] || currency
|
161
|
+
total + i[:regular_shipping].to_i
|
162
|
+
}.to_s
|
163
|
+
xml.price(:currency => currency) {
|
164
|
+
xml.text! shipping
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
@xml.dup
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns the signature for the cart XML.
|
175
|
+
def signature
|
176
|
+
@xml or checkout_xml
|
177
|
+
HMAC::SHA1.digest(@merchant_key, @xml)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns HTML for a checkout form for buying all the items in the
|
181
|
+
# cart.
|
182
|
+
def checkout_button(button_opts = {})
|
183
|
+
@xml or checkout_xml
|
184
|
+
burl = button_url(button_opts)
|
185
|
+
html = Builder::XmlMarkup.new(:indent => 2)
|
186
|
+
html.form(:action => submit_url, :style => 'border: 0;',
|
187
|
+
:id => 'BB_BuyButtonForm', :method => 'post',
|
188
|
+
:name => 'BB_BuyButtonForm') {
|
189
|
+
html.input(:name => 'cart', :type => 'hidden',
|
190
|
+
:value => Base64.encode64(@xml).gsub("\n", ''))
|
191
|
+
html.input(:name => 'signature', :type => 'hidden',
|
192
|
+
:value => Base64.encode64(signature).gsub("\n", ''))
|
193
|
+
html.input(:alt => 'Google Checkout',
|
194
|
+
:style => "width: auto;",
|
195
|
+
:src => button_url(button_opts),
|
196
|
+
:type => 'image')
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
# Given a set of options for the button, button_url returns the URL
|
201
|
+
# for the button image.
|
202
|
+
# The options are the same as those specified on
|
203
|
+
# http://checkout.google.com/seller/checkout_buttons.html , with a
|
204
|
+
# couple of extra options for convenience. Rather than specifying the
|
205
|
+
# width and height manually, you may specify :size to be one of
|
206
|
+
# :small, :medium, or :large, and that you may set :buy_or_checkout
|
207
|
+
# to :buy_now or :checkout to get a 'Buy Now' button versus a
|
208
|
+
# 'Checkout' button. If you don't specify :buy_or_checkout, the Cart
|
209
|
+
# will try to guess based on if the cart has more than one item in
|
210
|
+
# it. Whatever you don't pass will be filled in with the defaults from
|
211
|
+
# DefaultButtonOpts.
|
212
|
+
def button_url(opts = {})
|
213
|
+
opts = DefaultButtonOpts.merge opts
|
214
|
+
opts[:buy_or_checkout] ||= @contents.size > 1 ? :checkout :
|
215
|
+
:buy_now
|
216
|
+
opts.merge! ButtonSizes[opts[:buy_or_checkout]][opts[:size]]
|
217
|
+
bname = opts[:buy_or_checkout] == :buy_now ? 'buy.gif' :
|
218
|
+
'checkout.gif'
|
219
|
+
opts.delete :size
|
220
|
+
opts.delete :buy_or_checkout
|
221
|
+
opts[:merchant_id] = @merchant_id
|
222
|
+
|
223
|
+
path = opts.map { |k,v| "#{k}=#{v}" }.join('&')
|
224
|
+
|
225
|
+
"http://#{CheckoutURL}/buttons/#{bname}?#{path}"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.8
|
3
|
+
specification_version: 1
|
4
|
+
name: google_checkout
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-12-17
|
8
|
+
summary: An interface to the Google Checkout API.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: pete.elmore@gmail.com
|
12
|
+
homepage: http://debu.gs/google-checkout
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: google-checkout
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
authors:
|
27
|
+
- Pete Elmore
|
28
|
+
files:
|
29
|
+
- doc/LICENSE
|
30
|
+
- doc/README
|
31
|
+
- lib/google-checkout.rb
|
32
|
+
test_files: []
|
33
|
+
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
extra_rdoc_files:
|
37
|
+
- doc/README
|
38
|
+
- doc/LICENSE
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
requirements: []
|
44
|
+
|
45
|
+
dependencies: []
|
46
|
+
|