nbudin-google4r-checkout 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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