google4r-checkout 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -2
- data/README +1 -19
- data/lib/google4r/checkout/commands.rb +208 -23
- data/lib/google4r/checkout/frontend.rb +62 -2
- data/lib/google4r/checkout/notifications.rb +302 -11
- data/lib/google4r/checkout/shared.rb +3 -3
- data/lib/google4r/checkout/xml_generation.rb +429 -225
- data/test/unit/add_merchant_order_number_command_test.rb +210 -0
- data/test/unit/add_tracking_data_command_test.rb +225 -0
- data/test/unit/archive_order_command_test.rb +198 -0
- data/test/unit/authorization_amount_notification_test.rb +207 -0
- data/test/unit/authorize_order_command_test.rb +198 -0
- data/test/unit/cancel_order_command_test.rb +83 -0
- data/test/unit/charge_amount_notification_test.rb +64 -0
- data/test/unit/charge_order_command_test.rb +77 -0
- data/test/unit/chargeback_amount_notification_test.rb +195 -0
- data/test/unit/checkout_command_xml_generator_test.rb +12 -4
- data/test/unit/deliver_order_command_test.rb +70 -0
- data/test/unit/frontend_test.rb +14 -1
- data/test/unit/notification_handler_test.rb +28 -8
- data/test/unit/order_state_change_notification_test.rb +1 -2
- data/test/unit/refund_amount_notification_test.rb +195 -0
- data/test/unit/refund_order_command_test.rb +258 -0
- data/test/unit/risk_information_notification_test.rb +98 -0
- data/test/unit/send_buyer_message_command_test.rb +219 -0
- data/test/unit/unarchive_order_command_test.rb +198 -0
- metadata +17 -2
@@ -262,7 +262,7 @@ module Google4R #:nodoc:
|
|
262
262
|
end
|
263
263
|
|
264
264
|
unit_price = (element.elements['unit-price'].text.to_f * 100).to_i
|
265
|
-
unit_price_currency = element.elements['unit-price
|
265
|
+
unit_price_currency = element.elements['unit-price'].attributes['currency']
|
266
266
|
result.unit_price = Money.new(unit_price, unit_price_currency)
|
267
267
|
|
268
268
|
return result
|
@@ -516,7 +516,7 @@ module Google4R #:nodoc:
|
|
516
516
|
# with the Area as the only parameter. The created area is returned in any case.
|
517
517
|
#
|
518
518
|
# The area to be created depends on the given parameter clazz. It can be one
|
519
|
-
# of { UsCountryArea, UsStateArea, UsZipArea }.
|
519
|
+
# of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
|
520
520
|
#
|
521
521
|
# Raises a RuntimeError if the parameter clazz is invalid.
|
522
522
|
#
|
@@ -527,7 +527,7 @@ module Google4R #:nodoc:
|
|
527
527
|
# area.area = UsCountryArea::ALL
|
528
528
|
# end
|
529
529
|
def create_excluded_area(clazz, &block)
|
530
|
-
raise "Invalid Area class: #{clazz}!" unless [ UsCountryArea, UsStateArea, UsZipArea ].include?(clazz)
|
530
|
+
raise "Invalid Area class: #{clazz}!" unless [ PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea ].include?(clazz)
|
531
531
|
|
532
532
|
area = clazz.new
|
533
533
|
@excluded_areas << area
|
@@ -32,6 +32,62 @@ require 'rexml/document'
|
|
32
32
|
|
33
33
|
module Google4R #:nodoc:
|
34
34
|
module Checkout #:nodoc:
|
35
|
+
|
36
|
+
# Abstract super class for all CommandXmlGenerators
|
37
|
+
# It should never be instantiated directly
|
38
|
+
class CommandXmlGenerator
|
39
|
+
|
40
|
+
# The list of command tag names
|
41
|
+
COMMAND_TO_TAG =
|
42
|
+
{
|
43
|
+
ChargeOrderCommand => "charge-order",
|
44
|
+
RefundOrderCommand => "refund-order",
|
45
|
+
CancelOrderCommand => "cancel-order",
|
46
|
+
AuthorizeOrderCommand => "authorize-order",
|
47
|
+
ProcessOrderCommand => "process-order",
|
48
|
+
AddMerchantOrderNumberCommand => "add-merchant-order-number",
|
49
|
+
DeliverOrderCommand => "deliver-order",
|
50
|
+
AddTrackingDataCommand => "add-tracking-data",
|
51
|
+
SendBuyerMessageCommand => "send-buyer-message",
|
52
|
+
ArchiveOrderCommand => "archive-order",
|
53
|
+
UnarchiveOrderCommand => "unarchive-order"
|
54
|
+
}
|
55
|
+
|
56
|
+
def initialize(command)
|
57
|
+
@command = command
|
58
|
+
end
|
59
|
+
|
60
|
+
# Base method to generate the XML for a particular command
|
61
|
+
def generate
|
62
|
+
@document = REXML::Document.new
|
63
|
+
|
64
|
+
declaration = REXML::XMLDecl.new
|
65
|
+
declaration.encoding = 'utf-8'
|
66
|
+
@document << declaration
|
67
|
+
|
68
|
+
self.process_command(@command)
|
69
|
+
|
70
|
+
io = StringIO.new
|
71
|
+
@document.write(io, 0) # TODO: Maybe replace 0 by -1 so no spaces are inserted?
|
72
|
+
return io.string
|
73
|
+
end
|
74
|
+
|
75
|
+
def tag_name_for_command(klass)
|
76
|
+
COMMAND_TO_TAG[klass]
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
# Base method to generate root tag of a command
|
82
|
+
def process_command(command)
|
83
|
+
tag_name = self.tag_name_for_command(command.class)
|
84
|
+
root = @document.add_element(tag_name)
|
85
|
+
root.add_attribute('google-order-number', command.google_order_number)
|
86
|
+
root.add_attribute('xmlns', 'http://checkout.google.com/schema/2')
|
87
|
+
return root;
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
35
91
|
# Use the CheckoutXmlGenerator to create an XML document from a CheckoutCommand
|
36
92
|
# object.
|
37
93
|
#
|
@@ -46,268 +102,416 @@ module Google4R #:nodoc:
|
|
46
102
|
#--
|
47
103
|
# TODO: Refactor the big, monolitic generator into smaller, easier testable ones. One for each major part of the resulting XML document. This will also reduce the overhead in generating other types of XML documents.
|
48
104
|
#++
|
49
|
-
class CheckoutCommandXmlGenerator
|
50
|
-
# Initializes the CheckoutCommandXmlGenerator with the CheckoutCommand it is
|
51
|
-
# to conver to XML.
|
52
|
-
def initialize(checkout_command)
|
53
|
-
@checkout_command = checkout_command
|
54
|
-
end
|
105
|
+
class CheckoutCommandXmlGenerator < CommandXmlGenerator
|
55
106
|
|
56
|
-
|
57
|
-
|
58
|
-
def
|
59
|
-
@document
|
107
|
+
protected
|
108
|
+
|
109
|
+
def process_command(command)
|
110
|
+
root = @document.add_element("checkout-shopping-cart" , { 'xmlns' => 'http://checkout.google.com/schema/2' })
|
60
111
|
|
61
|
-
|
62
|
-
declaration.encoding = 'utf-8'
|
63
|
-
@document << declaration
|
112
|
+
self.process_shopping_shopping_cart(root, command.shopping_cart)
|
64
113
|
|
65
|
-
|
114
|
+
# <merchant-checkout-flow-support>
|
115
|
+
flow_element = root.add_element('checkout-flow-support').add_element('merchant-checkout-flow-support')
|
116
|
+
|
117
|
+
# <tax-tables>
|
118
|
+
if command.tax_tables then
|
119
|
+
self.process_tax_tables(command.tax_tables, flow_element)
|
120
|
+
end
|
121
|
+
|
122
|
+
# <continue-shopping-url>
|
123
|
+
if not command.continue_shopping_url.nil? then
|
124
|
+
flow_element.add_element('continue-shopping-url').text = command.continue_shopping_url
|
125
|
+
end
|
66
126
|
|
67
|
-
|
68
|
-
|
69
|
-
|
127
|
+
# <edit-cart-url>
|
128
|
+
if not command.edit_cart_url.nil? then
|
129
|
+
flow_element.add_element('edit-cart-url').text = command.edit_cart_url
|
130
|
+
end
|
131
|
+
|
132
|
+
# <request-buyer-phone-number>
|
133
|
+
if not command.request_buyer_phone_number.nil? then
|
134
|
+
flow_element.add_element('request-buyer-phone-number').text =
|
135
|
+
if command.request_buyer_phone_number then
|
136
|
+
"true"
|
137
|
+
else
|
138
|
+
"false"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# <shipping-methods>
|
143
|
+
shippings_element = flow_element.add_element('shipping-methods')
|
144
|
+
command.shipping_methods.each do |shipping_method|
|
145
|
+
self.process_shipping_method(shippings_element, shipping_method)
|
146
|
+
end
|
70
147
|
end
|
71
148
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
self.process_shopping_shopping_cart(root, command.shopping_cart)
|
78
|
-
|
79
|
-
# <merchant-checkout-flow-support>
|
80
|
-
flow_element = root.add_element('checkout-flow-support').add_element('merchant-checkout-flow-support')
|
149
|
+
# adds the tax-tables to the parent xml element
|
150
|
+
# assumes that the first member of the tax_tables array is the default tax rule table
|
151
|
+
# and that all others are alternate tax rules
|
152
|
+
def process_tax_tables(tax_tables, parent)
|
153
|
+
tax_tables_element = parent.add_element('tax-tables')
|
81
154
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
155
|
+
# assumes that the first tax table is the default
|
156
|
+
default_table = tax_tables.first
|
157
|
+
|
158
|
+
default_table_element = tax_tables_element.add_element('default-tax-table')
|
159
|
+
rules_element = default_table_element.add_element('tax-rules')
|
86
160
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# <edit-cart-url>
|
93
|
-
if not command.edit_cart_url.nil? then
|
94
|
-
flow_element.add_element('edit-cart-url').text = command.edit_cart_url
|
95
|
-
end
|
96
|
-
|
97
|
-
# <request-buyer-phone-number>
|
98
|
-
if not command.request_buyer_phone_number.nil? then
|
99
|
-
flow_element.add_element('request-buyer-phone-number').text =
|
100
|
-
if command.request_buyer_phone_number then
|
101
|
-
"true"
|
102
|
-
else
|
103
|
-
"false"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# <shipping-methods>
|
108
|
-
shippings_element = flow_element.add_element('shipping-methods')
|
109
|
-
command.shipping_methods.each do |shipping_method|
|
110
|
-
self.process_shipping_method(shippings_element, shipping_method)
|
111
|
-
end
|
161
|
+
default_table.rules.each do |rule|
|
162
|
+
default_rule_element = rules_element.add_element('default-tax-rule')
|
163
|
+
default_rule_element.add_element('shipping-taxed').text=rule.shipping_taxed.to_s
|
164
|
+
default_rule_element.add_element('rate').text=rule.rate.to_s
|
165
|
+
self.process_area(default_rule_element.add_element('tax-area'), rule.area)
|
112
166
|
end
|
113
|
-
|
114
|
-
# adds the tax-tables to the parent xml element
|
115
|
-
# assumes that the first member of the tax_tables array is the default tax rule table
|
116
|
-
# and that all others are alternate tax rules
|
117
|
-
def process_tax_tables(tax_tables, parent)
|
118
|
-
tax_tables_element = parent.add_element('tax-tables')
|
119
|
-
|
120
|
-
# assumes that the first tax table is the default
|
121
|
-
default_table = tax_tables.first
|
122
|
-
|
123
|
-
default_table_element = tax_tables_element.add_element('default-tax-table')
|
124
|
-
rules_element = default_table_element.add_element('tax-rules')
|
125
|
-
|
126
|
-
default_table.rules.each do |rule|
|
127
|
-
default_rule_element = rules_element.add_element('default-tax-rule')
|
128
|
-
default_rule_element.add_element('shipping-taxed').text=rule.shipping_taxed.to_s
|
129
|
-
default_rule_element.add_element('rate').text=rule.rate.to_s
|
130
|
-
self.process_area(default_rule_element.add_element('tax-area'), rule.area)
|
131
|
-
end
|
132
167
|
|
133
|
-
|
134
|
-
|
135
|
-
|
168
|
+
# populate alternate tax tables
|
169
|
+
alt_tables = tax_tables.last(tax_tables.length-1)
|
170
|
+
alt_tables_element = tax_tables_element.add_element('alternate-tax-tables')
|
136
171
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
172
|
+
alt_tables.each do |table|
|
173
|
+
table_element = alt_tables_element.add_element('alternate-tax-table')
|
174
|
+
table_element.add_attribute('name', table.name)
|
175
|
+
table_element.add_attribute('standalone', table.standalone.to_s)
|
141
176
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
177
|
+
rules_element = table_element.add_element('alternate-tax-rules')
|
178
|
+
table.rules.each do |rule|
|
179
|
+
alt_rule_element = rules_element.add_element('alternate-tax-rule')
|
180
|
+
alt_rule_element.add_element('rate').text=rule.rate.to_s
|
146
181
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
182
|
+
self.process_area(alt_rule_element.add_element('tax-area'), rule.area)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
151
186
|
|
152
187
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
# add <merchant-private-data> to the cart if any has been set
|
163
|
-
if not shopping_cart.private_data.nil? then
|
164
|
-
self.process_hash(cart_element.add_element('merchant-private-data'), shopping_cart.private_data)
|
165
|
-
end
|
166
|
-
|
167
|
-
# process the items in the cart
|
168
|
-
items_element = cart_element.add_element('items')
|
169
|
-
shopping_cart.items.each do |item|
|
170
|
-
self.process_item(items_element, item)
|
171
|
-
end
|
188
|
+
def process_shopping_shopping_cart(parent, shopping_cart)
|
189
|
+
cart_element = parent.add_element('shopping-cart')
|
190
|
+
|
191
|
+
# add <cart-expiration> tag to the cart if a time has been added to the cart
|
192
|
+
if not shopping_cart.expires_at.nil? then
|
193
|
+
cart_element.add_element('cart-expiration').add_element('good-until-date').text =
|
194
|
+
shopping_cart.expires_at.iso8601
|
172
195
|
end
|
173
196
|
|
174
|
-
#
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
item_element.add_element('item-name').text = item.name
|
179
|
-
item_element.add_element('item-description').text = item.description
|
180
|
-
|
181
|
-
item_element.add_element('unit-price', { 'currency' => item.unit_price.currency }).text = item.unit_price.to_s
|
182
|
-
item_element.add_element('quantity').text = item.quantity.to_i
|
183
|
-
|
184
|
-
if not item.id.nil? then
|
185
|
-
item_element.add_element('merchant-item-id').text = item.id
|
186
|
-
end
|
187
|
-
|
188
|
-
if not item.private_data.nil? then
|
189
|
-
self.process_hash(item_element.add_element('merchant-private-item-data'), item.private_data)
|
190
|
-
end
|
191
|
-
|
192
|
-
# The above was easy; now we need to get the appropriate tax table for this
|
193
|
-
# item. The Item class makes sure that the table exists.
|
194
|
-
if not item.tax_table.nil? then
|
195
|
-
item_element.add_element('tax-table-selector').text = item.tax_table.name
|
196
|
-
end
|
197
|
+
# add <merchant-private-data> to the cart if any has been set
|
198
|
+
if not shopping_cart.private_data.nil? then
|
199
|
+
self.process_hash(cart_element.add_element('merchant-private-data'), shopping_cart.private_data)
|
197
200
|
end
|
198
201
|
|
199
|
-
#
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
202
|
+
# process the items in the cart
|
203
|
+
items_element = cart_element.add_element('items')
|
204
|
+
shopping_cart.items.each do |item|
|
205
|
+
self.process_item(items_element, item)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Adds an <item> tag to the tag parent with the appropriate values.
|
210
|
+
def process_item(parent, item)
|
211
|
+
item_element = parent.add_element('item')
|
212
|
+
|
213
|
+
item_element.add_element('item-name').text = item.name
|
214
|
+
item_element.add_element('item-description').text = item.description
|
215
|
+
|
216
|
+
item_element.add_element('unit-price', { 'currency' => item.unit_price.currency }).text = item.unit_price.to_s
|
217
|
+
item_element.add_element('quantity').text = item.quantity.to_i
|
218
|
+
|
219
|
+
if not item.id.nil? then
|
220
|
+
item_element.add_element('merchant-item-id').text = item.id
|
221
|
+
end
|
222
|
+
|
223
|
+
if not item.private_data.nil? then
|
224
|
+
self.process_hash(item_element.add_element('merchant-private-item-data'), item.private_data)
|
225
|
+
end
|
226
|
+
|
227
|
+
# The above was easy; now we need to get the appropriate tax table for this
|
228
|
+
# item. The Item class makes sure that the table exists.
|
229
|
+
if not item.tax_table.nil? then
|
230
|
+
item_element.add_element('tax-table-selector').text = item.tax_table.name
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Adds an item for the given shipping method.
|
235
|
+
def process_shipping_method(parent, shipping_method)
|
236
|
+
if shipping_method.kind_of? PickupShipping then
|
237
|
+
process_pickup_shipping(parent, shipping_method)
|
238
|
+
elsif shipping_method.kind_of? FlatRateShipping then
|
239
|
+
process_flat_rate_shipping(parent, shipping_method)
|
240
|
+
else
|
241
|
+
raise "Unknown ShippingMethod type of #{shipping_method.inspect}!"
|
208
242
|
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def process_flat_rate_shipping(parent, shipping)
|
246
|
+
element = parent.add_element('flat-rate-shipping')
|
247
|
+
element.add_attribute('name', shipping.name)
|
248
|
+
element.add_element('price', { 'currency' => shipping.price.currency }).text = shipping.price.to_s
|
209
249
|
|
210
|
-
|
211
|
-
|
212
|
-
element.add_attribute('name', shipping.name)
|
213
|
-
element.add_element('price', { 'currency' => shipping.price.currency }).text = shipping.price.to_s
|
250
|
+
if shipping.excluded_areas.length + shipping.allowed_areas.length > 0 then
|
251
|
+
restrictions_tag = element.add_element('shipping-restrictions')
|
214
252
|
|
215
|
-
if shipping.
|
216
|
-
|
253
|
+
if shipping.allowed_areas.length > 0 then
|
254
|
+
allowed_tag = restrictions_tag.add_element('allowed-areas')
|
217
255
|
|
218
|
-
|
219
|
-
allowed_tag
|
220
|
-
|
221
|
-
shipping.allowed_areas.each do |area|
|
222
|
-
self.process_area(allowed_tag, area)
|
223
|
-
end
|
256
|
+
shipping.allowed_areas.each do |area|
|
257
|
+
self.process_area(allowed_tag, area)
|
224
258
|
end
|
225
|
-
|
226
|
-
|
227
|
-
|
259
|
+
end
|
260
|
+
|
261
|
+
if shipping.excluded_areas.length > 0 then
|
262
|
+
excluded_tag = restrictions_tag.add_element('excluded-areas')
|
228
263
|
|
229
|
-
|
230
|
-
|
231
|
-
end
|
264
|
+
shipping.excluded_areas.each do |area|
|
265
|
+
self.process_area(excluded_tag, area)
|
232
266
|
end
|
233
267
|
end
|
234
268
|
end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
269
|
+
end
|
270
|
+
|
271
|
+
def process_pickup_shipping(parent, shipping)
|
272
|
+
element = parent.add_element('pickup')
|
273
|
+
element.add_attribute('name', shipping.name)
|
274
|
+
element.add_element('price', { 'currency' => shipping.price.currency }).text = shipping.price.to_s
|
275
|
+
end
|
276
|
+
|
277
|
+
# Adds an appropriate tag for the given Area subclass instance to the parent Element.
|
278
|
+
def process_area(parent, area)
|
279
|
+
if area.kind_of? UsZipArea then
|
280
|
+
parent.add_element('us-zip-area').add_element('zip-pattern').text = area.pattern
|
281
|
+
elsif area.kind_of? UsCountryArea then
|
282
|
+
parent.add_element('us-country-area', { 'country-area' => area.area })
|
283
|
+
elsif area.kind_of? UsStateArea then
|
284
|
+
parent.add_element('us-state-area').add_element('state').text = area.state
|
285
|
+
elsif area.kind_of? WorldArea then
|
286
|
+
parent.add_element('world-area')
|
287
|
+
elsif area.kind_of? PostalArea then
|
288
|
+
postal_area_element = parent.add_element('postal-area')
|
289
|
+
postal_area_element.add_element('country-code').text = area.country_code
|
290
|
+
if area.postal_code_pattern then
|
255
291
|
postal_area_element.add_element('postal-code-pattern').text = area.postal_code_pattern
|
256
|
-
else
|
257
|
-
raise "Area of unknown type: #{area.inspect}."
|
258
292
|
end
|
293
|
+
else
|
294
|
+
raise "Area of unknown type: #{area.inspect}."
|
259
295
|
end
|
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
|
-
|
296
|
+
end
|
297
|
+
|
298
|
+
# Converts a Hash into an XML structure. The keys are converted to tag names. If
|
299
|
+
# the values are Hashs themselves then process_hash is called upon them. If the
|
300
|
+
# values are Arrays then a new element with the key's name will be created.
|
301
|
+
#
|
302
|
+
# If a value is an Array then this array will be flattened before it is processed.
|
303
|
+
# Thus, nested arrays are not allowed.
|
304
|
+
#
|
305
|
+
# === Example
|
306
|
+
#
|
307
|
+
# process_hash(parent, { 'foo' => { 'bar' => 'baz' } })
|
308
|
+
#
|
309
|
+
# # will produce a structure that is equivalent to.
|
310
|
+
#
|
311
|
+
# <foo>
|
312
|
+
# <bar>baz</bar>
|
313
|
+
# </foo>
|
314
|
+
#
|
315
|
+
#
|
316
|
+
# process_hash(parent, { 'foo' => [ { 'bar' => 'baz' }, "d'oh", 2 ] })
|
317
|
+
#
|
318
|
+
# # will produce a structure that is equivalent to.
|
319
|
+
#
|
320
|
+
# <foo>
|
321
|
+
# <bar>baz</bar>
|
322
|
+
# </foo>
|
323
|
+
# <foo>d&</foo>
|
324
|
+
# <foo>2</foo>
|
325
|
+
def process_hash(parent, hash)
|
326
|
+
hash.each do |key, value|
|
327
|
+
if value.kind_of? Array then
|
328
|
+
value.flatten.each do |arr_entry|
|
329
|
+
if arr_entry.kind_of? Hash then
|
330
|
+
self.process_hash(parent.add_element(self.str2tag_name(key.to_s)), arr_entry)
|
331
|
+
else
|
332
|
+
parent.add_element(self.str2tag_name(key.to_s)).text = arr_entry.to_s
|
297
333
|
end
|
298
|
-
elsif value.kind_of? Hash then
|
299
|
-
process_hash(parent.add_element(self.str2tag_name(key.to_s)), value)
|
300
|
-
else
|
301
|
-
parent.add_element(self.str2tag_name(key.to_s)).text = value.to_s
|
302
334
|
end
|
335
|
+
elsif value.kind_of? Hash then
|
336
|
+
process_hash(parent.add_element(self.str2tag_name(key.to_s)), value)
|
337
|
+
else
|
338
|
+
parent.add_element(self.str2tag_name(key.to_s)).text = value.to_s
|
303
339
|
end
|
304
340
|
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# Converts a string to a valid XML tag name. Whitespace will be converted into a dash/minus
|
344
|
+
# sign, non alphanumeric characters that are neither "-" nor "_" nor ":" will be stripped.
|
345
|
+
def str2tag_name(str)
|
346
|
+
str.gsub(%r{\s}, '-').gsub(%r{[^a-zA-Z0-9\-\_:]}, '')
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
class ChargeOrderCommandXmlGenerator < CommandXmlGenerator
|
351
|
+
|
352
|
+
protected
|
353
|
+
|
354
|
+
def process_command(command)
|
355
|
+
root = super
|
356
|
+
process_money(root, command.amount) if command.amount
|
357
|
+
end
|
358
|
+
|
359
|
+
# add the amount element to the charge command
|
360
|
+
def process_money(parent, money)
|
361
|
+
amount_element = parent.add_element('amount')
|
362
|
+
amount_element.text = money.to_s
|
363
|
+
amount_element.add_attribute('currency', money.currency)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
class RefundOrderCommandXmlGenerator < CommandXmlGenerator
|
368
|
+
|
369
|
+
protected
|
370
|
+
|
371
|
+
def process_command(command)
|
372
|
+
root = super
|
373
|
+
process_money(root, command.amount) if command.amount
|
374
|
+
process_comment(root, command.comment) if command.comment
|
375
|
+
process_reason(root, command.reason)
|
376
|
+
end
|
377
|
+
|
378
|
+
# add the amount element to the refund command
|
379
|
+
def process_money(parent, money)
|
380
|
+
amount_element = parent.add_element('amount')
|
381
|
+
amount_element.text = money.to_s
|
382
|
+
amount_element.add_attribute('currency', money.currency)
|
383
|
+
end
|
384
|
+
|
385
|
+
# add the comment element to the refund command
|
386
|
+
def process_comment(parent, comment)
|
387
|
+
comment_element = parent.add_element('comment')
|
388
|
+
comment_element.text = comment
|
389
|
+
end
|
390
|
+
|
391
|
+
# add the reason element to the refund command
|
392
|
+
def process_reason(parent, reason)
|
393
|
+
reason_element = parent.add_element('reason')
|
394
|
+
reason_element.text = reason
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
class CancelOrderCommandXmlGenerator < CommandXmlGenerator
|
399
|
+
|
400
|
+
protected
|
401
|
+
|
402
|
+
def process_command(command)
|
403
|
+
root = super
|
404
|
+
root.add_element('reason').text = command.reason
|
305
405
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
406
|
+
if command.comment then
|
407
|
+
root.add_element('comment').text = command.comment
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
412
|
+
|
413
|
+
class AuthorizeOrderCommandXmlGenerator < CommandXmlGenerator
|
414
|
+
|
415
|
+
protected
|
416
|
+
|
417
|
+
def process_command(command)
|
418
|
+
super
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
class ProcessOrderCommandXmlGenerator < CommandXmlGenerator
|
423
|
+
|
424
|
+
protected
|
425
|
+
|
426
|
+
def process_command(command)
|
427
|
+
super
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
class AddMerchantOrderNumberCommandXmlGenerator < CommandXmlGenerator
|
432
|
+
|
433
|
+
protected
|
434
|
+
|
435
|
+
def process_command(command)
|
436
|
+
root = super
|
437
|
+
process_merchant_order_number(root, command.merchant_order_number)
|
438
|
+
end
|
439
|
+
|
440
|
+
def process_merchant_order_number(parent, merchant_order_number)
|
441
|
+
merchant_order_number_element = parent.add_element('merchant-order-number')
|
442
|
+
merchant_order_number_element.text = merchant_order_number
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
class DeliverOrderCommandXmlGenerator < CommandXmlGenerator
|
447
|
+
|
448
|
+
protected
|
449
|
+
|
450
|
+
def process_command(command)
|
451
|
+
root = super
|
452
|
+
# Add tracking info
|
453
|
+
process_tracking_data(root, command.carrier, command.tracking_number)
|
454
|
+
root.add_element('send-email').text = command.send_email.to_s
|
455
|
+
end
|
456
|
+
|
457
|
+
def process_tracking_data(parent, carrier, tracking_number)
|
458
|
+
if carrier and tracking_number then
|
459
|
+
element = parent.add_element('tracking-data')
|
460
|
+
element.add_element('carrier').text = carrier
|
461
|
+
element.add_element('tracking-number').text = tracking_number
|
310
462
|
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
class AddTrackingDataCommandXmlGenerator < CommandXmlGenerator
|
467
|
+
|
468
|
+
protected
|
469
|
+
|
470
|
+
def process_command(command)
|
471
|
+
root = super
|
472
|
+
# Add tracking info
|
473
|
+
process_tracking_data(root, command.carrier, command.tracking_number)
|
474
|
+
end
|
475
|
+
|
476
|
+
def process_tracking_data(parent, carrier, tracking_number)
|
477
|
+
if carrier and tracking_number then
|
478
|
+
element = parent.add_element('tracking-data')
|
479
|
+
element.add_element('carrier').text = carrier
|
480
|
+
element.add_element('tracking-number').text = tracking_number
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
class SendBuyerMessageCommandXmlGenerator < CommandXmlGenerator
|
486
|
+
|
487
|
+
protected
|
488
|
+
|
489
|
+
def process_command(command)
|
490
|
+
root = super
|
491
|
+
root.add_element('message').text = command.message
|
492
|
+
if not command.send_email.nil? then
|
493
|
+
root.add_element('send-email').text = command.send_email.to_s
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
class ArchiveOrderCommandXmlGenerator < CommandXmlGenerator
|
499
|
+
|
500
|
+
protected
|
501
|
+
|
502
|
+
def process_command(command)
|
503
|
+
super
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
class UnarchiveOrderCommandXmlGenerator < CommandXmlGenerator
|
508
|
+
|
509
|
+
protected
|
510
|
+
|
511
|
+
def process_command(command)
|
512
|
+
super
|
513
|
+
end
|
311
514
|
end
|
515
|
+
|
312
516
|
end
|
313
517
|
end
|