nbudin-google4r-checkout 1.0.6

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.
Files changed (79) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGES +83 -0
  3. data/LICENSE +22 -0
  4. data/README +58 -0
  5. data/Rakefile +80 -0
  6. data/VERSION +1 -0
  7. data/lib/google4r/checkout/commands.rb +515 -0
  8. data/lib/google4r/checkout/frontend.rb +204 -0
  9. data/lib/google4r/checkout/merchant_calculation.rb +321 -0
  10. data/lib/google4r/checkout/notifications.rb +708 -0
  11. data/lib/google4r/checkout/shared.rb +1249 -0
  12. data/lib/google4r/checkout/utils.rb +94 -0
  13. data/lib/google4r/checkout/xml_generation.rb +920 -0
  14. data/lib/google4r/checkout.rb +34 -0
  15. data/test/frontend_configuration.rb +13 -0
  16. data/test/integration/checkout_command_test.rb +246 -0
  17. data/test/test_helper.rb +115 -0
  18. data/test/unit/add_merchant_order_number_command_test.rb +70 -0
  19. data/test/unit/add_tracking_data_command_test.rb +75 -0
  20. data/test/unit/address_test.rb +131 -0
  21. data/test/unit/anonymous_address_test.rb +75 -0
  22. data/test/unit/archive_order_command_test.rb +66 -0
  23. data/test/unit/area_test.rb +44 -0
  24. data/test/unit/authorization_amount_notification_test.rb +69 -0
  25. data/test/unit/authorize_order_command_test.rb +66 -0
  26. data/test/unit/backorder_items_command_test.rb +83 -0
  27. data/test/unit/callback_handler_test.rb +83 -0
  28. data/test/unit/cancel_items_command_test.rb +89 -0
  29. data/test/unit/cancel_order_command_test.rb +83 -0
  30. data/test/unit/carrier_calculated_shipping_test.rb +57 -0
  31. data/test/unit/charge_amount_notification_test.rb +64 -0
  32. data/test/unit/charge_order_command_test.rb +77 -0
  33. data/test/unit/chargeback_amount_notification_test.rb +65 -0
  34. data/test/unit/checkout_command_test.rb +125 -0
  35. data/test/unit/checkout_command_xml_generator_test.rb +218 -0
  36. data/test/unit/command_test.rb +116 -0
  37. data/test/unit/deliver_order_command_test.rb +70 -0
  38. data/test/unit/delivery_method_test.rb +42 -0
  39. data/test/unit/digital_content_test.rb +105 -0
  40. data/test/unit/flat_rate_shipping_test.rb +132 -0
  41. data/test/unit/frontend_test.rb +136 -0
  42. data/test/unit/item_info_test.rb +69 -0
  43. data/test/unit/item_test.rb +171 -0
  44. data/test/unit/marketing_preferences_test.rb +65 -0
  45. data/test/unit/merchant_calculated_shipping_test.rb +172 -0
  46. data/test/unit/merchant_calculation_callback_test.rb +137 -0
  47. data/test/unit/merchant_calculation_result_test.rb +78 -0
  48. data/test/unit/merchant_calculation_results_test.rb +178 -0
  49. data/test/unit/merchant_code_result_test.rb +51 -0
  50. data/test/unit/merchant_code_test.rb +122 -0
  51. data/test/unit/new_order_notification_test.rb +115 -0
  52. data/test/unit/notification_acknowledgement_test.rb +67 -0
  53. data/test/unit/notification_handler_test.rb +113 -0
  54. data/test/unit/order_adjustment_test.rb +119 -0
  55. data/test/unit/order_state_change_notification_test.rb +158 -0
  56. data/test/unit/pickup_shipping_test.rb +70 -0
  57. data/test/unit/postal_area_test.rb +71 -0
  58. data/test/unit/private_data_parser_test.rb +68 -0
  59. data/test/unit/refund_amount_notification_test.rb +65 -0
  60. data/test/unit/refund_order_command_test.rb +86 -0
  61. data/test/unit/reset_items_shipping_information_command_test.rb +83 -0
  62. data/test/unit/return_items_command_test.rb +83 -0
  63. data/test/unit/risk_information_notification_test.rb +98 -0
  64. data/test/unit/send_buyer_message_command_test.rb +73 -0
  65. data/test/unit/ship_items_command_test.rb +101 -0
  66. data/test/unit/shipping_adjustment_test.rb +100 -0
  67. data/test/unit/shopping_cart_test.rb +146 -0
  68. data/test/unit/tax_rule_test.rb +70 -0
  69. data/test/unit/tax_table_test.rb +82 -0
  70. data/test/unit/tracking_data_test.rb +54 -0
  71. data/test/unit/unarchive_order_command_test.rb +66 -0
  72. data/test/unit/us_country_area_test.rb +76 -0
  73. data/test/unit/us_state_area_test.rb +70 -0
  74. data/test/unit/us_zip_area_test.rb +66 -0
  75. data/test/unit/world_area_test.rb +48 -0
  76. data/test/xml/apiv2.xsd +997 -0
  77. data/test/xml/test_check_persisting_works_expected.xml +213 -0
  78. data/var/cacert.pem +7815 -0
  79. metadata +200 -0
@@ -0,0 +1,1249 @@
1
+ #--
2
+ # Project: google4r
3
+ # File: lib/google4r/checkout/shared.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 and modules that are shared by the notification
28
+ # handling and parsing as well as the command generating code.
29
+
30
+ #--
31
+ # TODO: Make the optional attributes return defaults that make sense, i.e. Money.new(0)?
32
+ #++
33
+ module Google4R #:nodoc:
34
+ module Checkout #:nodoc:
35
+ # This exception is thrown by Command#send_to_google_checkout when an error occured.
36
+ class GoogleCheckoutError < Exception
37
+ # The serial number of the error returned by Google.
38
+ attr_reader :serial_number
39
+
40
+ # The HTTP response code of Google's response.
41
+ attr_reader :response_code
42
+
43
+ # The parameter is a hash with the entries :serial_number, :message and :response_code.
44
+ # The attributes serial_number, message and response_code are set to the values in the
45
+ # Hash.
46
+ def initialize(hash)
47
+ @response_code = hash[:response_code]
48
+ @message = hash[:message]
49
+ @serial_number = hash[:serial_number]
50
+ end
51
+
52
+ # Returns a human readable representation of the Exception with the message, HTTP
53
+ # response code and serial number as returned by Google checkout.
54
+ def to_s
55
+ "GoogleCheckoutError: message = '#{@message}', response code = '#{@response_code}', serial number = '#{@serial_number}'."
56
+ end
57
+ end
58
+
59
+ # ShoppingCart instances are containers for Item instances. You can add
60
+ # Items to the class using #create_item (see the documentation of this
61
+ # method for an example).
62
+ class ShoppingCart
63
+ # The owner of this cart. At the moment, this always is the CheckoutCartCommand.
64
+ attr_reader :owner
65
+
66
+ # The items in the cart. Do not modify this array directly but use
67
+ # #create_item to add items.
68
+ attr_reader :items
69
+
70
+ # You can set the <cart-expiration> time with this property. If left
71
+ # unset then the tag will not be generated and the cart will never
72
+ # expire.
73
+ attr_accessor :expires_at
74
+
75
+ # You can set almost arbitrary data into the cart using this method.
76
+ #
77
+ # The data will be converted to XML in the following way: The keys are converted
78
+ # to tag names (whitespace becomes "-", all chars not matching /[a-zA-Z0-9\-_])/
79
+ # will be removed.
80
+ #
81
+ # If a value is an array then the key for this value will be used as the tag
82
+ # name for each of the arrays's entries.
83
+ #
84
+ # Arrays will be flattened before it is processed.
85
+ #
86
+ # === Example
87
+ #
88
+ # cart.private_data = { 'foo' => { 'bar' => 'baz' } })
89
+ #
90
+ # # will produce the following XML
91
+ #
92
+ # <foo>
93
+ # <bar>baz</bar>
94
+ # </foo>
95
+ #
96
+ #
97
+ # cart.private_data = { 'foo' => [ { 'bar' => 'baz' }, "d'oh", 2 ] }
98
+ #
99
+ # # will produce the following XML
100
+ #
101
+ # <foo>
102
+ # <bar>baz</bar>
103
+ # </foo>
104
+ # <foo>d&amp;</foo>
105
+ # <foo>2</foo>
106
+ attr_reader :private_data
107
+
108
+ # Sets the value of the private_data attribute.
109
+ def private_data=(value)
110
+ raise "The given value #{value.inspect} is not a Hash!" unless value.kind_of?(Hash)
111
+ @private_data = value
112
+ end
113
+
114
+ # Initialize a new ShoppingCart with an empty Array for the items.
115
+ def initialize(owner)
116
+ @owner = owner
117
+ @items = Array.new
118
+ end
119
+
120
+ # Use this method to add a new item to the cart. If you use a block with
121
+ # this method then the block will be given the new item. The new item
122
+ # will be returned in any case.
123
+ #
124
+ # Passing a block is the preferred way of using this method.
125
+ #
126
+ # === Example
127
+ #
128
+ # # Using a block (preferred).
129
+ # cart = ShoppingCart.new
130
+ #
131
+ # cart.create_item do |item|
132
+ # item.name = "Dry Food Pack"
133
+ # item.description = "A pack of highly nutritious..."
134
+ # item.unit_price = Money.new(3500, "USD") # $35.00
135
+ # item.quantity = 1
136
+ # end
137
+ #
138
+ # # Not using a block.
139
+ # cart = ShoppingCart.new
140
+ #
141
+ # item = cart.create_item
142
+ # item.name = "Dry Food Pack"
143
+ # item.description = "A pack of highly nutritious..."
144
+ # item.unit_price = Money.new(3500, "USD") # $35.00
145
+ # item.quantity = 1
146
+ def create_item(&block)
147
+ item = Item.new(self)
148
+ @items << item
149
+
150
+ # Pass the newly generated item to the given block to set its attributes.
151
+ yield(item) if block_given?
152
+
153
+ return item
154
+ end
155
+
156
+ # Creates a new ShoppingCart object from a REXML::Element object.
157
+ def self.create_from_element(element, owner)
158
+ result = ShoppingCart.new(owner)
159
+
160
+ text = element.elements['cart-expiration/good-until-date'].text rescue nil
161
+ result.expires_at = Time.parse(text) unless text.nil?
162
+
163
+ data_element = element.elements['merchant-private-data']
164
+ value = PrivateDataParser.element_to_value(data_element) unless data_element.nil?
165
+
166
+ result.private_data = value unless value.nil?
167
+
168
+ element.elements.each('items/item') do |item_element|
169
+ result.items << Item.create_from_element(item_element, result)
170
+ end
171
+
172
+ return result
173
+ end
174
+ end
175
+
176
+ # An Item object represents a line of goods in the shopping cart/receipt.
177
+ #
178
+ # You should never initialize them directly but use ShoppingCart#create_item instead.
179
+ #
180
+ # Note that you have to create/set the tax tables for the owner of the cart in which
181
+ # the item is before you can set the tax_table attribute.
182
+ class Item
183
+ # The cart that this item belongs to.
184
+ attr_reader :shopping_cart
185
+
186
+ # The name of the cart item (string, required).
187
+ attr_accessor :name
188
+
189
+ # The description of the cart item (string, required).
190
+ attr_accessor :description
191
+
192
+ # The price for one unit of the given good (Money instance, required).
193
+ attr_reader :unit_price
194
+
195
+ # Sets the price for one unit of goods described by this item. money must respond to
196
+ # :cents and :currency as the Money class does.
197
+ def unit_price=(money)
198
+ if not (money.respond_to?(:cents) and money.respond_to?(:currency)) then
199
+ raise "Invalid price - does not respond to :cents and :currency - #{money.inspect}."
200
+ end
201
+
202
+ @unit_price = money
203
+ end
204
+
205
+ # The weigth of the cart item (Weight, required when carrier calculated
206
+ # shipping is used)
207
+ attr_reader :weight
208
+
209
+ # Sets the weight of this item
210
+ def weight=(weight)
211
+ raise "Invalid object type for weight" unless weight.kind_of? Weight
212
+ @weight = weight
213
+ end
214
+
215
+
216
+ # Number of units that this item represents (integer, required).
217
+ attr_accessor :quantity
218
+
219
+ # Optional string value that is used to store the item's id (defined by the merchant)
220
+ # in the cart. Serialized to <merchant-item-id> in XML. Displayed by Google Checkout.
221
+ attr_accessor :id
222
+
223
+ # Optional hash value that is used to store the item's id (defined by the merchant)
224
+ # in the cart. Serialized to <merchant-private-item-data> in XML. Not displayed by
225
+ # Google Checkout.
226
+ #
227
+ # Must be a Hash. See ShoppingCart#private_data on how the serialization to XML is
228
+ # done.
229
+ attr_reader :private_data
230
+
231
+ # Sets the private data for this item.
232
+ def private_data=(value)
233
+ raise "The given value #{value.inspect} is not a Hash!" unless value.kind_of?(Hash)
234
+ @private_data = value
235
+ end
236
+
237
+ # The tax table to use for this item. Optional.
238
+ attr_reader :tax_table
239
+
240
+ # Sets the tax table to use for this item. When you set this attribute using this
241
+ # method then the used table must already be added to the cart. Otherwise, a
242
+ # RuntimeError will be raised.
243
+ def tax_table=(table)
244
+ raise "The table #{table.inspect} is not in the item's cart yet!" unless shopping_cart.owner.tax_tables.include?(table)
245
+
246
+ @tax_table = table
247
+ end
248
+
249
+ # DigitalContent information for this item. Optional.
250
+ attr_reader :digital_content
251
+
252
+ def create_digital_content(digital_content=nil, &block)
253
+
254
+ if @digital_content.nil?
255
+ if digital_content.nil?
256
+ @digital_content = DigitalContent.new
257
+ else
258
+ @digital_content = digital_content
259
+ end
260
+ end
261
+
262
+ if block_given?
263
+ yield @digital_content
264
+ end
265
+
266
+ return @digital_content
267
+ end
268
+
269
+ # Subscription information for this item. Optional.
270
+ attr_reader :subscription
271
+
272
+ def create_subscription(subscription=nil, &block)
273
+
274
+ if @subscription.nil?
275
+ if subscription.nil?
276
+ @subscription = Subscription.new
277
+ else
278
+ @subscription = subscription
279
+ end
280
+ end
281
+
282
+ if block_given?
283
+ yield @subscription
284
+ end
285
+
286
+ return @subscription
287
+ end
288
+
289
+ # Create a new Item in the given Cart. You should not instantize this class directly
290
+ # but use Cart#create_item instead.
291
+ def initialize(shopping_cart)
292
+ @shopping_cart = shopping_cart
293
+ end
294
+
295
+ # Creates a new Item object from a REXML::Element object.
296
+ def self.create_from_element(element, shopping_cart)
297
+ result = Item.new(shopping_cart)
298
+
299
+ result.name = element.elements['item-name'].text
300
+ result.description = element.elements['item-description'].text
301
+ result.quantity = element.elements['quantity'].text.to_i
302
+ result.id = element.elements['merchant-item-id'].text rescue nil
303
+
304
+ weight_element = element.elements['item-weight']
305
+ if not weight_element.nil?
306
+ result.weight = Weight.create_from_element(weight_element)
307
+ end
308
+
309
+ data_element = element.elements['merchant-private-item-data']
310
+ if not data_element.nil? then
311
+ value = PrivateDataParser.element_to_value(data_element)
312
+ result.private_data = value unless value.nil?
313
+ end
314
+
315
+ table_selector = element.elements['tax-table-selector'].text rescue nil
316
+ if not table_selector.nil? then
317
+ result.tax_table = shopping_cart.owner.tax_tables.find {|table| table.name == table_selector }
318
+ end
319
+
320
+ unit_price = (element.elements['unit-price'].text.to_f * 100).to_i
321
+ unit_price_currency = element.elements['unit-price'].attributes['currency']
322
+ result.unit_price = Money.new(unit_price, unit_price_currency)
323
+
324
+ digital_content_element = element.elements['digital-content']
325
+ if not digital_content_element.nil?
326
+ result.create_digital_content(DigitalContent.create_from_element(digital_content_element))
327
+ end
328
+
329
+ return result
330
+ end
331
+
332
+ # A DigitalContent item represents the information relating to online delivery of digital items
333
+ #
334
+ # You should never initialize it directly but use Item#digital_content instead
335
+ #
336
+ # See http://code.google.com/apis/checkout/developer/Google_Checkout_Digital_Delivery.html
337
+ # for information on Google Checkout's idea of digital content.
338
+ #
339
+ # item.digital_content do |dc|
340
+ # dc.optimistic!
341
+ # dc.description = %{Here's some information on how to get your content}
342
+ # end
343
+ class DigitalContent
344
+
345
+ # Constants for display-disposition
346
+ OPTIMISTIC = 'OPTIMISTIC'
347
+ PESSIMISTIC = 'PESSIMISTIC'
348
+
349
+ # A description of how the user should access the digital content
350
+ # after completing the order (string, required for description-based
351
+ # delivery, otherwise optional)
352
+ attr_accessor :description
353
+
354
+ # Either 'OPTIMISTIC' or 'PESSIMISTIC'. If OPTIMISTIC, then Google
355
+ # will display instructions for accessing the digital content as soon
356
+ #as the buyer confirms the order. Optional, but default is PESSIMISTIC
357
+ attr_reader :display_disposition
358
+
359
+ def display_disposition=(disposition)
360
+ raise "display_disposition can only be set to PESSIMISTIC or OPTIMISTIC" unless disposition == OPTIMISTIC || disposition == PESSIMISTIC
361
+ @display_disposition = disposition
362
+ end
363
+
364
+ # A boolean identifying whether email delivery is used for this item.
365
+ attr_accessor :email_delivery
366
+
367
+ # A key required by the user to access this digital content after completing the order (string, optional)
368
+ attr_accessor :key
369
+
370
+ # A URL required by the user to access this digital content after completing the order (string, optional)
371
+ attr_accessor :url
372
+
373
+ def initialize
374
+ @display_disposition = PESSIMISTIC
375
+ end
376
+
377
+ # Creates a new DigitalContent object from a REXML::Element object
378
+ def self.create_from_element(element)
379
+ result = DigitalContent.new
380
+ result.description = element.elements['description'].text rescue nil
381
+ result.display_disposition = element.elements['display-disposition'].text rescue nil
382
+ result.email_delivery = element.elements['email-delivery'].text rescue nil # TODO need to convert to boolean?
383
+ result.key = element.elements['key'].text rescue nil
384
+ result.url = element.elements['url'].text rescue nil
385
+ return result
386
+ end
387
+ end
388
+
389
+ class Subscription
390
+
391
+ # Constants for period
392
+ DAILY = 'DAILY'
393
+ WEEKLY = 'WEEKLY'
394
+ SEMI_MONTHLY = 'SEMI_MONTHLY'
395
+ MONTHLY = 'MONTHLY'
396
+ EVERY_TWO_MONTHS = 'EVERY_TWO_MONTHS'
397
+ QUARTERLY = 'QUARTERLY'
398
+ YEARLY = 'YEARLY'
399
+
400
+ # Constants for type
401
+ MERCHANT = 'merchant'
402
+ GOOGLE = 'google'
403
+
404
+ # Optional. The no-charge-after attribute specifies the latest date and time that
405
+ # you can charge the customer for the subscription. This element can help you to
406
+ # ensure that you do not overcharge your customers.
407
+ attr_accessor :no_charge_after
408
+
409
+ # Required. The period attribute specifies how frequently you will charge the
410
+ # customer for the subscription. Valid values for this attribute are DAILY,
411
+ # WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, and YEARLY.
412
+ attr_reader :period
413
+
414
+ def period=(period)
415
+ unless [DAILY, WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, YEARLY].include?(period)
416
+ raise "period can only be set to DAILY, WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, or YEARLY"
417
+ end
418
+ @period = period
419
+ end
420
+
421
+ # Optional. The start-date attribute specifies the date that the subscription's
422
+ # recurrence period will begin. Like all dates in Checkout, this is in ISO 8601
423
+ # format. If you set the <unit-price> tag's value to a nonzero value, then the
424
+ # start-date for the subscription will automatically be set to the time that is
425
+ # exactly one recurrence period after the order is placed.
426
+ attr_accessor :start_date
427
+
428
+ # Required. The type attribute identifies the type of subscription that you are
429
+ # creating. The valid values for this attribute are merchant and google, and this
430
+ # specifies who handles the recurrences. The merchant value specifies
431
+ # Merchant-Handled recurrences, and the google value specifies Google-Handled
432
+ # recurrences.
433
+ attr_reader :type
434
+
435
+ def type=(type)
436
+ unless [MERCHANT, GOOGLE].include?(type)
437
+ raise "type can only be set to MERCHANT or GOOGLE"
438
+ end
439
+ @type = type
440
+ end
441
+
442
+ # Container for payments
443
+ attr_reader :payments
444
+
445
+ def add_payment(&block)
446
+ payment = SubscriptionPayment.new(self)
447
+ @payments << payment
448
+
449
+ # Pass the newly generated payment to the given block to set its attributes.
450
+ yield(payment) if block_given?
451
+
452
+ return payment
453
+ end
454
+
455
+ # Container for recurrent items
456
+ attr_reader :recurrent_items
457
+
458
+ def add_recurrent_item(&block)
459
+ item = RecurrentItem.new(self)
460
+ @recurrent_items << item
461
+
462
+ # Pass the newly generated item to the given block to set its attributes.
463
+ yield(item) if block_given?
464
+
465
+ return item
466
+ end
467
+
468
+ def initialize
469
+ @payments = []
470
+ @recurrent_items = []
471
+ end
472
+
473
+ def self.create_from_element(element)
474
+ result = Subscription.new
475
+ result.no_charge_after = Time.iso8601(element.attributes['no-charge-after']) rescue nil
476
+ result.period = element.attributes['period'] rescue nil
477
+ result.start_date = Time.iso8601(element.attributes['start-date']) rescue nil
478
+ result.type = element.attributes['type'] rescue nil
479
+
480
+ element.elements['payments/subscription-payment'].each do |payment_element|
481
+ result.payments << SubscriptionPayment.create_from_element(subscription, payment_element)
482
+ end
483
+
484
+ element.elements['recurrent-item'].each do |item_element|
485
+ result.recurrent_items << Item.create_from_element(item_element)
486
+ end
487
+
488
+ return result
489
+ end
490
+
491
+ class SubscriptionPayment
492
+
493
+ attr_accessor :subscription
494
+
495
+ # Optional. The times attribute indicates how many times you will charge the
496
+ # customer for a defined subscription payment. A subscription may have multiple
497
+ # payment schedules, and the times attribute lets you indicate how many times
498
+ # each charge will be assessed. For example, you might charge the customer a
499
+ # reduced rate for the first three months of a subscription and then charge the
500
+ # standard rate each month thereafter.
501
+ attr_accessor :times
502
+
503
+ # The maximum amount that you will be allowed to charge the customer, including
504
+ # tax, for all recurrences (Money instance, required).
505
+ attr_reader :maximum_charge
506
+
507
+ def initialize(subscription)
508
+ @subscription = subscription
509
+ end
510
+
511
+ # Sets the maximum charge for this subscription payment. money must respond to
512
+ # :cents and :currency as the Money class does.
513
+ def maximum_charge=(money)
514
+ if not (money.respond_to?(:cents) and money.respond_to?(:currency)) then
515
+ raise "Invalid price - does not respond to :cents and :currency - #{money.inspect}."
516
+ end
517
+
518
+ @maximum_charge = money
519
+ end
520
+
521
+ def self.create_from_element(subscription, element)
522
+ result = SubscriptionPayment.new
523
+ result.subscription = subscription
524
+ result.times = element.attributes['times'].to_i rescue nil
525
+
526
+ maximum_charge = (element.elements['maximum-charge'].text.to_f * 100).to_i
527
+ maximum_charge_currency = element.elements['maximum-charge'].attributes['currency']
528
+ result.maximum_charge = Money.new(maximum_charge, maximum_charge_currency)
529
+
530
+ return result
531
+ end
532
+ end
533
+
534
+ class RecurrentItem < Item
535
+
536
+ attr_accessor :subscription
537
+
538
+ def initialize(subscription)
539
+ @subscription = subscription
540
+ end
541
+
542
+ def self.create_from_element(element, subscription)
543
+ item = super(element, nil)
544
+
545
+ result = RecurrentItem.new(subscription)
546
+ result.description = item.description
547
+ result.digital_content = item.digital_content
548
+ result.id = item.id
549
+ result.name = item.name
550
+ result.private_data = item.private_data
551
+ result.quantity = item.quantity
552
+ result.tax_table = item.tax_table
553
+ result.unit_price = item.unit_price
554
+ result.weight = item.weight
555
+
556
+ return result
557
+ end
558
+
559
+ end
560
+ end
561
+ end
562
+
563
+ # A TaxTable is an ordered array of TaxRule objects. You should create the TaxRule
564
+ # instances using #create_rule
565
+ #
566
+ # You must set up a tax table factory and should only create tax tables from within
567
+ # its temporal factory method as described in the class documentation of Frontend.
568
+ #
569
+ # Each tax table must have one or more tax rules.
570
+ #
571
+ # === Example
572
+ #
573
+ # include Google4R::Checkout
574
+ #
575
+ # tax_free_table = TaxTable.new(false)
576
+ # tax_free_table.name = "default table"
577
+ # tax_free_table.create_rule do |rule|
578
+ # rule.area = UsCountryArea.new(UsCountryArea::ALL)
579
+ # rule.rate = 0.0
580
+ # end
581
+ class TaxTable
582
+ # The name of this tax table (string, required).
583
+ attr_accessor :name
584
+
585
+ # An Array of the TaxRule objects that this TaxTable contains. Use #create_rule do
586
+ # add to this Array but do not change it directly.
587
+ attr_reader :rules
588
+
589
+ # Boolean, true iff the table's standalone attribute is to be set to "true".
590
+ attr_reader :standalone
591
+
592
+ def initialize(standalone)
593
+ @rules = Array.new
594
+
595
+ @standalone = standalone
596
+ end
597
+
598
+ # Use this method to add a new TaxRule to the table. If you use a block with
599
+ # this method then the block will called with the newly created rule for the
600
+ # parameter. The method will return the new rule in any case.
601
+ def create_rule(&block)
602
+ rule = TaxRule.new(self)
603
+ @rules << rule
604
+
605
+ # Pass the newly generated rule to the given block to set its attributes.
606
+ yield(rule) if block_given?
607
+
608
+ return rule
609
+ end
610
+ end
611
+
612
+ # A TaxRule specifies which taxes to apply in which area. Have a look at the "Google
613
+ # Checkout documentation" [http://code.google.com/apis/checkout/developer/index.html#specifying_tax_info]
614
+ # for more information.
615
+ class TaxRule
616
+ # The table this rule belongs to.
617
+ attr_reader :table
618
+
619
+ # The tax rate for this rule (double, required).
620
+ attr_accessor :rate
621
+
622
+ # The area where this tax rule applies (Area subclass instance, required). Serialized
623
+ # to <tax-area> in XML.
624
+ attr_accessor :area
625
+
626
+ # If shipping should be taxed with this tax rule (boolean, defaults to false)
627
+ attr_accessor :shipping_taxed
628
+
629
+ # Creates a new TaxRule in the given TaxTable. Do no call this method yourself
630
+ # but use TaxTable#create_rule instead!
631
+ def initialize(table)
632
+ @table = table
633
+ @shipping_taxed = false
634
+ end
635
+ end
636
+
637
+ # Abstract class for areas that are used to specify a tax area. Do not use this class
638
+ # but only its subclasses.
639
+ class Area
640
+ # Mark this class as abstract by throwing a RuntimeError on initialization.
641
+ def initialize #:nodoc:
642
+ raise "Do not use the abstract class Google::Checkout::Area!"
643
+ end
644
+ end
645
+
646
+ # Instances of UsZipArea represent areas specified by US ZIPs and ZIP patterns.
647
+ class UsZipArea < Area
648
+ # The pattern for this ZIP area.
649
+ attr_accessor :pattern
650
+
651
+ # You can optionally initialize the Area with its value.
652
+ def initialize(pattern=nil)
653
+ self.pattern = pattern unless pattern.nil?
654
+ end
655
+ end
656
+
657
+ # Instances of WorldArea represent a tax area that applies globally.
658
+ class WorldArea < Area
659
+ def initialize
660
+ end
661
+ end
662
+
663
+ # Instances of PostalArea represent a geographical region somewhere in the world.
664
+ class PostalArea < Area
665
+
666
+ # String; The two-letter ISO 3166 country code.
667
+ attr_accessor :country_code
668
+
669
+ # String; Postal code or a range of postal codes for a specific country. To specify a
670
+ # range of postal codes, use an asterisk as a wildcard operator. For example,
671
+ # you can provide a postal_code_pattern value of "SW*" to indicate that a shipping
672
+ # option is available or a tax rule applies in any postal code beginning with the
673
+ # characters SW.
674
+ #
675
+ # === Example
676
+ #
677
+ # area = PostalArea.new('DE')
678
+ # area.postal_code_pattern = '10*'
679
+ attr_accessor :postal_code_pattern
680
+
681
+ # === Parameters
682
+ #
683
+ # country_code should be a two-letter ISO 3166 country code
684
+ # postal_code_pattern should be a full or partial postcode string, using * as a wildcard
685
+ def initialize(country_code=nil, postal_code_pattern=nil)
686
+ @country_code = country_code
687
+ @postal_code_pattern = postal_code_pattern
688
+ end
689
+ end
690
+
691
+ # Instances of UsStateArea represent states in the US.
692
+ class UsStateArea < Area
693
+ # The two-letter code of the US state.
694
+ attr_reader :state
695
+
696
+ # You can optionally initialize the Area with its value.
697
+ def initialize(state=nil)
698
+ @state = state unless state.nil?
699
+ end
700
+
701
+ # Writer for the state attribute. value must match /^[A-Z]{2,2}$/.
702
+ def state=(value)
703
+ raise "Invalid US state: #{value}" unless value =~ /^[A-Z]{2,2}$/
704
+ @state = value
705
+ end
706
+ end
707
+
708
+ # Instances of UsCountryArea identify a region within the US.
709
+ class UsCountryArea < Area
710
+ CONTINENTAL_48 = "CONTINENTAL_48".freeze
711
+ FULL_50_STATES = "FULL_50_STATES".freeze
712
+ ALL = "ALL".freeze
713
+
714
+ # The area that is specified with this UsCountryArea (required). Can be
715
+ # one of UsCountryArea::CONTINENTAL_48, UsCountryArea::FULL_50_STATES
716
+ # and UsCountryArea::ALL.
717
+ # See the Google Checkout API for information on these values.
718
+ attr_reader :area
719
+
720
+ # You can optionally initialize the Area with its value.
721
+ def initialize(area=nil)
722
+ self.area = area unless area.nil?
723
+ end
724
+
725
+ # Writer for the area attribute. value must be one of CONTINENTAL_48,
726
+ # FULL_50_STATES and ALL
727
+ def area=(value)
728
+ raise "Invalid area :#{value}!" unless [ CONTINENTAL_48, FULL_50_STATES, ALL ].include?(value)
729
+ @area = value
730
+ end
731
+ end
732
+
733
+ # Abstract class for delivery methods
734
+ class DeliveryMethod
735
+ # The name of the shipping method (string, required).
736
+ attr_accessor :name
737
+
738
+ # The price of the shipping method (Money instance, required).
739
+ attr_reader :price
740
+
741
+ # Sets the cost for this shipping method. money must respond to :cents and :currency
742
+ # as Money objects would.
743
+ def price=(money)
744
+ if not (money.respond_to?(:cents) and money.respond_to?(:currency)) then
745
+ raise "Invalid cost - does not respond to :cents and :currency - #{money.inspect}."
746
+ end
747
+
748
+ @price = money
749
+ end
750
+
751
+ # Mark this class as abstract by throwing a RuntimeError on initialization.
752
+ def initialize
753
+ raise "Do not use the abstract class Google::Checkout::ShippingMethod!"
754
+ end
755
+ end
756
+
757
+ # Abstract class for shipping methods. Do not use this class directly but only
758
+ # one of its subclasses.
759
+ class ShippingMethod < DeliveryMethod
760
+ # An Array of allowed areas for shipping-restrictions of this shipping instance. Use
761
+ # #create_allowed_area to add to this area but do not change it directly.
762
+ attr_reader :shipping_restrictions_allowed_areas
763
+
764
+ # An Array of excluded areas for shipping-restrictions of this shipping instance. Use
765
+ # #create_excluded_area to add to this area but do not change it directly.
766
+ attr_reader :shipping_restrictions_excluded_areas
767
+
768
+ def initialize
769
+ @shipping_restrictions_allowed_areas = Array.new
770
+ @shipping_restrictions_excluded_areas = Array.new
771
+ end
772
+
773
+ # This method create a new instance of subclass of Area and put it
774
+ # in the array determined by the two symbols provided. The valid
775
+ # symbols for the first two parameters are:
776
+ #
777
+ # type : :shipping_restrictions, :address_filters
778
+ # areas : :allowed_areas, :excluded_areas
779
+ #
780
+ # The third parameter clazz is used to specify the type of
781
+ # Area you want to create. It can be one
782
+ # of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
783
+ #
784
+ # Raises a RuntimeError if the parameter clazz is invalid.
785
+ #
786
+ # If you passed a block (preferred) then the block is called
787
+ # with the Area as the only parameter.
788
+ #
789
+ # === Example
790
+ #
791
+ # method = MerchantCalculatedShipping.new
792
+ # method.create_area(:shipping_restrictions, :allowed_areas, UsCountryArea) do |area|
793
+ # area.area = UsCountryArea::ALL
794
+ # end
795
+ def create_area(type, areas, clazz, &block)
796
+ areas_array_name = "@#{type.to_s + '_' + areas.to_s}"
797
+ areas = instance_variable_get(areas_array_name)
798
+ raise "Undefined instance variable: #{areas_array_name}" unless areas.nil? == false
799
+ raise "Invalid Area class: #{clazz}!" unless [ PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea ].include?(clazz)
800
+ area = clazz.new
801
+ areas << area
802
+
803
+ yield(area) if block_given?
804
+
805
+ return area
806
+ end
807
+
808
+ # Creates a new Area, adds it to the internal list of allowed areas for shipping
809
+ # restrictions. If you passed a block (preferred) then the block is called
810
+ # with the Area as the only parameter.
811
+ #
812
+ # The area to be created depends on the given parameter clazz. It can be one
813
+ # of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
814
+ #
815
+ # Raises a RuntimeError if the parameter clazz is invalid.
816
+ #
817
+ # === Example
818
+ #
819
+ # method = FlatRateShipping.new
820
+ # method.create_allowed_area(UsCountryArea) do |area|
821
+ # area.area = UsCountryArea::ALL
822
+ # end
823
+ def create_allowed_area(clazz, &block)
824
+ return create_area(:shipping_restrictions, :allowed_areas, clazz, &block)
825
+ end
826
+
827
+ # Creates a new Area, adds it to the internal list of excluded areas for shipping
828
+ # restrictions. If you passed a block (preferred) then the block is called
829
+ # with the Area as the only parameter. The created area is returned in any case.
830
+ #
831
+ # The area to be created depends on the given parameter clazz. It can be one
832
+ # of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
833
+ #
834
+ # Raises a RuntimeError if the parameter clazz is invalid.
835
+ #
836
+ # === Example
837
+ #
838
+ # method = FlatRateShipping.new
839
+ # method.create_excluded_area(UsCountryArea) do |area|
840
+ # area.area = UsCountryArea::ALL
841
+ # end
842
+ def create_excluded_area(clazz, &block)
843
+ return create_area(:shipping_restrictions, :excluded_areas, clazz, &block)
844
+ end
845
+
846
+ alias :create_shipping_restrictions_allowed_area :create_allowed_area
847
+ alias :create_shipping_restrictions_excluded_area :create_excluded_area
848
+ end
849
+
850
+ # A class that represents the "pickup" shipping method.
851
+ class PickupShipping < DeliveryMethod
852
+ def initialize
853
+ end
854
+ end
855
+
856
+ # A class that represents the "flat_rate" shipping method.
857
+ class FlatRateShipping < ShippingMethod
858
+ def initialize
859
+ super
860
+ end
861
+ end
862
+
863
+ # A class that represents the "merchant-calculated" shipping method
864
+ class MerchantCalculatedShipping < ShippingMethod
865
+
866
+ # An Array of allowed areas for address-filters of this shipping instance. Use
867
+ # #create_allowed_area to add to this area but do not change it directly.
868
+ attr_reader :address_filters_allowed_areas
869
+
870
+ # An Array of excluded areas for address-filters of this shipping instance. Use
871
+ # #create_excluded_area to add to this area but do not change it directly.
872
+ attr_reader :address_filters_excluded_areas
873
+
874
+ def initialize
875
+ super
876
+ @address_filters_allowed_areas = Array.new
877
+ @address_filters_excluded_areas = Array.new
878
+ end
879
+
880
+ # Creates a new Area, adds it to the internal list of allowed areas for
881
+ # address filters. If you passed a block (preferred) then the block is
882
+ # called with the Area as the only parameter.
883
+ #
884
+ # The area to be created depends on the given parameter clazz. It can be one
885
+ # of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
886
+ #
887
+ # Raises a RuntimeError if the parameter clazz is invalid.
888
+ #
889
+ # === Example
890
+ #
891
+ # method = FlatRateShipping.new
892
+ # method.create_allowed_area(UsCountryArea) do |area|
893
+ # area.area = UsCountryArea::ALL
894
+ # end
895
+ def create_address_filters_allowed_area(clazz, &block)
896
+ return create_area(:address_filters, :allowed_areas, clazz, &block)
897
+ end
898
+
899
+ # Creates a new Area, adds it to the internal list of excluded areas for
900
+ # address filters. If you passed a block (preferred) then the block is
901
+ # called with the Area as the only parameter.
902
+ #
903
+ # The area to be created depends on the given parameter clazz. It can be one
904
+ # of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
905
+ #
906
+ # Raises a RuntimeError if the parameter clazz is invalid.
907
+ #
908
+ # === Example
909
+ #
910
+ # method = FlatRateShipping.new
911
+ # method.create_allowed_area(UsCountryArea) do |area|
912
+ # area.area = UsCountryArea::ALL
913
+ # end
914
+ def create_address_filters_allowed_area(clazz, &block)
915
+ return create_area(:address_filters, :allowed_areas, clazz, &block)
916
+ end
917
+ end
918
+
919
+ # A class that represents the "merchant-calculated" shipping method
920
+ class CarrierCalculatedShipping
921
+ # This encapsulates information about all of the shipping methods
922
+ # for which Google Checkout should obtain shipping costs.
923
+ attr_reader :carrier_calculated_shipping_options
924
+
925
+ # This encapsulates information about all of the packages that will be
926
+ # shipped to the buyer. At this time, merchants may only specify
927
+ # one package per order.
928
+ attr_reader :shipping_packages
929
+
930
+ def initialize()
931
+ @carrier_calculated_shipping_options = Array.new
932
+ @shipping_packages = Array.new
933
+ end
934
+
935
+ def create_carrier_calculated_shipping_option(&block)
936
+ option = CarrierCalculatedShippingOption.new(self)
937
+ @carrier_calculated_shipping_options << option
938
+
939
+ # Pass the newly generated rule to the given block to set its attributes.
940
+ yield(option) if block_given?
941
+
942
+ return option
943
+ end
944
+
945
+ def create_shipping_package(&block)
946
+ package = ShippingPackage.new(self)
947
+ @shipping_packages << package
948
+
949
+ # Pass the newly generated rule to the given block to set its attributes.
950
+ yield(package) if block_given?
951
+
952
+ return package
953
+ end
954
+
955
+ # Creates a new CarrierCalculatedShipping from the given
956
+ # REXML::Element instance.
957
+ # For testing only.
958
+ def create_from_element(element)
959
+ result = CarrierCalculatedShipping.new
960
+ element.elements.each('carrier-calculated-shipping-options/carrier-calculated-shipping-option') do |shipping_option_element|
961
+ result.carrier_calculated_shipping_options << CarrierCalculatedShippingOption.create_from_element(self, shipping_option_element)
962
+ end
963
+ element.elements.each('shipping-packages/shipping-package') do |shipping_package_element|
964
+ result.shipping_packages << ShippingPackage.create_from_element(self, shipping_package_element)
965
+ end
966
+ end
967
+
968
+ class CarrierCalculatedShippingOption < DeliveryMethod
969
+ # Constants for shipping company
970
+ FEDEX = 'FedEx'
971
+ UPS = 'UPS'
972
+ USPS = 'USPS'
973
+
974
+ # Constants for carrier pickup
975
+ DROP_OFF = 'DROP_OFF'
976
+ REGULAR_PICKUP = 'REGULAR_PICKUP'
977
+ SPECIAL_PICKUP = 'SPECIAL_PICKUP'
978
+
979
+ # The CarrierCalculatedShipping instance that this option belongs to.
980
+ attr_reader :carrier_calculated_shipping
981
+
982
+ # The name of the company that will ship the order.
983
+ # The only valid values for this tag are FedEx, UPS and USPS.
984
+ # (String, required)
985
+ alias :shipping_company :name
986
+ alias :shipping_company= :name=
987
+
988
+ # The shipping option that is being offered to the buyer
989
+ attr_accessor :shipping_type
990
+
991
+ # This specifies how the package will be transferred from the merchant
992
+ # to the shipper. Valid values for this tag are REGULAR_PICKUP,
993
+ # SPECIAL_PICKUP and DROP_OFF. The default value for this tag is DROP_OFF.
994
+ # (optional)
995
+ attr_accessor :carrier_pickup
996
+
997
+ # The fixed charge that will be added to the total cost of an order
998
+ # if the buyer selects the associated shipping option
999
+ # (Money, optional)
1000
+ attr_accessor :additional_fixed_charge
1001
+
1002
+ # The percentage amount by which a carrier-calculated shipping rate
1003
+ # will be adjusted. The tag's value may be positive or negative.
1004
+ # (Float, optional)
1005
+ attr_accessor :additional_variable_charge_percent
1006
+
1007
+ def initialize(carrier_calculated_shipping)
1008
+ @carrier_calculated_shipping = carrier_calculated_shipping
1009
+ #@carrier_pickup = DROP_OFF
1010
+ end
1011
+
1012
+ # Creates a new CarrierCalculatedShippingOption from the given
1013
+ # REXML::Element instance.
1014
+ # For testing only.
1015
+ def self.create_from_element(this_shipping, element)
1016
+ result = CarrierCalculatedShippingOption.new(this_shipping)
1017
+ result.shipping_company = element.elements['shipping-company'].text
1018
+ price = (element.elements['price'].text.to_f * 100).to_i
1019
+ price_currency = element.elements['price'].attributes['currency']
1020
+ result.price = Money.new(price, price_currency)
1021
+ result.shipping_type = element.elements['shipping-type']
1022
+ result.carrier_pickup = element.elements['carrier-pickup'] rescue nil
1023
+ result.additional_fixed_charge =
1024
+ element.elements['additional-fixed-charge'] rescue nil
1025
+ result.additional_variable_charge_percent =
1026
+ element.elements['additional-variable-charge-percent'] rescue nil
1027
+ end
1028
+ end
1029
+
1030
+ class ShippingPackage
1031
+ # Constants for delivery address category
1032
+ RESIDENTIAL = 'RESIDENTIAL'
1033
+ COMMERCIAL = 'COMMERCIAL'
1034
+
1035
+ # The CarrierCalculatedShipping instance that this package belongs to.
1036
+ attr_reader :carrier_calculated_shipping
1037
+
1038
+ # This contains information about the location from which an order
1039
+ # will be shipped. (AnonymousAddress)
1040
+ attr_accessor :ship_from
1041
+
1042
+ # This indicates whether the shipping method should be applied to
1043
+ # a residential or a commercial address. Valid values for this tag
1044
+ # are RESIDENTIAL and COMMERCIAL. (String, optional)
1045
+ attr_accessor :delivery_address_category
1046
+
1047
+ # This contains information about the height of the package being
1048
+ # shipped to the customer. (Google::Checktou::Dimension, optional)
1049
+ attr_accessor :height
1050
+
1051
+ # This contains information about the length of the package being
1052
+ # shipped to the customer. (Google::Checktou::Dimension, optional)
1053
+ attr_accessor :length
1054
+
1055
+ # This contains information about the width of the package being
1056
+ # shipped to the customer. (Google::Checktou::Dimension, optional)
1057
+ attr_accessor :width
1058
+
1059
+ def initialize(carrier_calculated_shipping)
1060
+ @carrier_calculated_shipping = carrier_calculated_shipping
1061
+ end
1062
+
1063
+ # Creates a new ShippingPackage from the given REXML::Element instance.
1064
+ # For testing only.
1065
+ def self.create_from_element(this_shipping, element)
1066
+ result = ShippingPackage.new(this_shipping)
1067
+ result.ship_from = ShipFromAddress.create_from_element(element.elements['ship-from'])
1068
+ result.delivery_address_category = element.elements['delivery-address-category'].text rescue nil
1069
+ result.height = element.elements['height'].text rescue nil
1070
+ result.length = element.elements['length'].text rescue nil
1071
+ result.width = element.elements['width'].text rescue nil
1072
+ return result
1073
+ end
1074
+ end
1075
+ end
1076
+
1077
+ # This is a base class for defining the unit of weight and dimension
1078
+ class Unit
1079
+ # This specifies the unit of measurement that corresponds to a shipping
1080
+ # package's length, width or height. The only valid value for
1081
+ # this attribute is IN.
1082
+ attr_accessor :unit
1083
+
1084
+ # This specifies the numeric value of a unit of measurement
1085
+ # corresponding to an item or a shipping package. (float)
1086
+ attr_accessor :value
1087
+
1088
+ def initialize
1089
+ raise "Google::Checkout::Unit is an abstract class!"
1090
+ end
1091
+
1092
+ # Creates a new Unit from the given REXML::Element instance.
1093
+ def self.create_from_element(element)
1094
+ result = self.new(element.attributes['value'].to_f)
1095
+ return result
1096
+ end
1097
+ end
1098
+
1099
+ # This defines package dimension
1100
+ class Dimension < Unit
1101
+
1102
+ # Constants for unit
1103
+ INCH = 'IN'
1104
+
1105
+ def initialize(value, unit=INCH)
1106
+ @unit = unit
1107
+ @value = value.to_f
1108
+ end
1109
+ end
1110
+
1111
+ # This defines item weight
1112
+ class Weight < Unit
1113
+ # Constants for unit
1114
+ LB = 'LB'
1115
+
1116
+ def initialize(value, unit=LB)
1117
+ @unit = unit
1118
+ @value = value.to_f
1119
+ end
1120
+ end
1121
+
1122
+ # This address is used in merchant calculation callback
1123
+ class AnonymousAddress
1124
+
1125
+ # The address ID (String)
1126
+ attr_accessor :address_id
1127
+
1128
+ # The buyer's city name (String).
1129
+ attr_accessor :city
1130
+
1131
+ # The buyers postal/zip code (String).
1132
+ attr_accessor :postal_code
1133
+
1134
+ # The buyer's geographical region (String).
1135
+ attr_accessor :region
1136
+
1137
+ # The buyer's country code (String, 2 chars, ISO 3166).
1138
+ attr_accessor :country_code
1139
+
1140
+ # Creates a new AnonymousAddress from the given REXML::Element instance.
1141
+ def self.create_from_element(element)
1142
+ result = AnonymousAddress.new
1143
+
1144
+ result.address_id = element.attributes['id']
1145
+ result.city = element.elements['city'].text
1146
+ result.country_code = element.elements['country-code'].text
1147
+ result.postal_code = element.elements['postal-code'].text
1148
+ result.region = element.elements['region'].text
1149
+ return result
1150
+ end
1151
+ end
1152
+
1153
+ # Address instances are used in NewOrderNotification objects for the buyer's billing
1154
+ # and buyer's shipping address.
1155
+ class Address < AnonymousAddress
1156
+ # Contact name (String, optional).
1157
+ attr_accessor :contact_name
1158
+
1159
+ # Second Address line (String).
1160
+ attr_accessor :address1
1161
+
1162
+ # Second Address line (String optional).
1163
+ attr_accessor :address2
1164
+
1165
+ # The buyer's city name (String).
1166
+ # attr_accessor :city
1167
+ # Now inherit from AnonymousAddress
1168
+
1169
+ # The buyer's company name (String; optional).
1170
+ attr_accessor :company_name
1171
+
1172
+ # The buyer's country code (String, 2 chars, ISO 3166).
1173
+ # attr_accessor :country_code
1174
+ # Now inherit from AnonymousAddress
1175
+
1176
+ # The buyer's email address (String; optional).
1177
+ attr_accessor :email
1178
+
1179
+ # The buyer's phone number (String; optional).
1180
+ attr_accessor :fax
1181
+
1182
+ # The buyer's phone number (String; Optional, can be enforced in CheckoutCommand).)
1183
+ attr_accessor :phone
1184
+
1185
+ # The buyers postal/zip code (String).
1186
+ # attr_accessor :postal_code
1187
+ # Now inherit from AnonymousAddress
1188
+
1189
+
1190
+ # The buyer's geographical region (String).
1191
+ # attr_accessor :region
1192
+ # Now inherit from AnonymousAddress
1193
+
1194
+ # Creates a new Address from the given REXML::Element instance.
1195
+ def self.create_from_element(element)
1196
+ result = Address.new
1197
+
1198
+ result.address1 = element.elements['address1'].text
1199
+ result.address2 = element.elements['address2'].text rescue nil
1200
+ result.city = element.elements['city'].text
1201
+ result.company_name = element.elements['company-name'].text rescue nil
1202
+ result.contact_name = element.elements['contact-name'].text rescue nil
1203
+ result.country_code = element.elements['country-code'].text
1204
+ result.email = element.elements['email'].text rescue nil
1205
+ result.fax = element.elements['fax'].text rescue nil
1206
+ result.phone = element.elements['phone'].text rescue nil
1207
+ result.postal_code = element.elements['postal-code'].text
1208
+ result.region = element.elements['region'].text
1209
+
1210
+ return result
1211
+ end
1212
+ end
1213
+
1214
+ # ItemInfo instances are used in Line-item shipping commands
1215
+ class ItemInfo
1216
+ # The merchant item id (String)
1217
+ attr_reader :merchant_item_id
1218
+
1219
+ # An array of tracking data for this item
1220
+ attr_reader :tracking_data_arr
1221
+
1222
+ def initialize(merchant_item_id)
1223
+ @merchant_item_id = merchant_item_id
1224
+ @tracking_data_arr = Array.new
1225
+ end
1226
+
1227
+ def create_tracking_data(carrier, tracking_number)
1228
+ tracking_data = TrackingData.new(carrier, tracking_number)
1229
+ @tracking_data_arr << tracking_data
1230
+ return tracking_data
1231
+ end
1232
+ end
1233
+
1234
+ # TrackingData instances are used in Line-item shipping commands
1235
+ class TrackingData
1236
+ # The name of the company responsible for shipping the item. Valid values
1237
+ # for this tag are DHL, FedEx, UPS, USPS and Other.
1238
+ attr_reader :carrier
1239
+
1240
+ # The shipper's tracking number that is associated with an order
1241
+ attr_reader :tracking_number
1242
+
1243
+ def initialize(carrier, tracking_number)
1244
+ @carrier = carrier.to_s
1245
+ @tracking_number = tracking_number.to_s
1246
+ end
1247
+ end
1248
+ end
1249
+ end