google4r-checkout-jn 1.1.jniziol

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