""" gchecky.model module describes the mapping between Google Checkout API (GC API) XML messages (GC API XML Schema) and data classes. This module uses L{gchecky.gxml} to automate much of the work so that the actual logic is not cluttered with machinery. The module code is simple and self-documenting. Please read the source code for simple description of the data-structures. Note that it tries to follow exactly the official GC API XML Schema. All the comments for the GC API are at U{Google Chackout API documentation }. Please consult it for any questions about GC API functioning. @author: etarassov @version: $Revision$ @contact: gchecky at gmail """ from gchecky import gxml from gchecky.data import CountryCode, PresentOrNot def test_document(doc, xml_text=None): """ Method used in doctests: ensures that a document is properly serialized. """ def normalize_xml(xml_text): """ Normalize the xml text to canonical form, so that two xml text chunks could be compared directly as strings. """ # If xml misses header, then add it. if len(xml_text) < 2 or xml_text[0:2] != "\n" + xml_text from xml.dom.minidom import parseString doc = parseString(xml_text) text = doc.toprettyxml('') # To avoid formatting problems we just strip all the line returns and # spaces (while it breaks XML into a text string it still makes # this form a 'canonical' one). return text.replace('\n', '').replace(' ', '') expected_xml = ((xml_text is not None) and normalize_xml(xml_text)) or None obtained_xml = normalize_xml(doc.toxml(pretty=' ')) if expected_xml is not None and expected_xml != obtained_xml: print "Expected:\n\n%s\n\nGot:\n\n%s\n" % (xml_text, doc.toxml(' ')) doc2 = gxml.Document.fromxml(doc.toxml()) if not (doc == doc2): print ''' Failed to correctly interpret the generated XML for this document: Original: %s Parsed: %s ''' % (doc.toxml(pretty=True), doc2.toxml()) def test_node(node, xml_text=None): """ Method used in doctests. Ensure that a node is properly serialized. """ class Dummy(gxml.Document): tag_name='dummy' data=gxml.Complex('node', node.__class__, required=True) if xml_text is not None: xml_text = xml_text.replace('<', ' <') # TODO ...just ugly (below) xml_text = "%s" % (xml_text,) test_document(Dummy(data=node), xml_text) CURRENCIES = ('USD', 'GBP') class price_t(gxml.Node): value = gxml.Double('', default=0) currency = gxml.String('@currency', values=CURRENCIES) DISPLAY_DISPOSITION = ('OPTIMISTIC', 'PESSIMISTIC') class digital_content_t(gxml.Node): description = gxml.Html('description', max_length=1024, required=False) email_delivery = gxml.Boolean('email-delivery', required=False) key = gxml.String('key', required=False) url = gxml.String('url', required=False) display_disposition = gxml.String('display-disposition', required=False, values=DISPLAY_DISPOSITION) class item_weight_t(gxml.Node): value = gxml.Double('@value') # , negative=False unit = gxml.String('@unit', values=('LB',)) class item_t(gxml.Node): """ >>> test_node(item_t(name='Peter', description='The Great', unit_price=price_t(value=1, currency='GBP'), quantity=1, merchant_item_id='custom_merchant_item_id', ... merchant_private_item_data=['some', {'private':'data', 'to':['test','the'],'thing':None}, '!! Numbers: ', None, False, True, [11, 12., [13.4]]]) ... ) >>> test_node(item_t(name='Name with empty description', description='', unit_price=price_t(value=1, currency='GBP'), quantity=1)) """ name = gxml.String('item-name') description = gxml.String('item-description') unit_price = gxml.Complex('unit-price', price_t) quantity = gxml.Decimal('quantity') item_weight = gxml.Complex('item-weight', item_weight_t, required=False) merchant_item_id = gxml.String('merchant-item-id', required=False) tax_table_selector = gxml.String('tax-table-selector', required=False) digital_content = gxml.Complex('digital-content', digital_content_t, required=False) merchant_private_item_data = gxml.Any('merchant-private-item-data', save_node_and_xml=True, required=False) class postal_area_t(gxml.Node): """ >>> test_node(postal_area_t(country_code = 'VU'), ... ''' ... VU ... ''' ... ) """ country_code = CountryCode('country-code') postal_code_pattern = gxml.String('postal-code-pattern', required=False) class tax_area_t(gxml.Node): world_area = PresentOrNot('world-area', required=False) postal_area = gxml.Complex('postal-area', postal_area_t, required=False) us_state = gxml.String('us-state-area/state', required=False) us_zip_pattern = gxml.String('us-zip-area/zip-pattern', required=False) # regex: [a-zA-Z0-9_]+\*? Note the optional asterisk us_country_area = gxml.String('us-country-area/country-area', values=('CONTINENTAL_48', 'FULL_50_STATES', 'ALL'), required=False) # enum('CONTINENTAL_48', 'FULL_50_STATES', 'ALL') class areas_t(gxml.Node): """ Represents a list of regions. >>> test_node( ... areas_t( ... states = ['LA', 'NY'], ... country_areas = ['ALL', 'CONTINENTAL_48'] ... ) ... , ... ''' ... ... ... LA ... ... ... NY ... ... ... ALL ... ... ... CONTINENTAL_48 ... ... ... ''' ... ) """ states = gxml.List('', gxml.String('us-state-area/state'), required=False) zip_patterns = gxml.List('', gxml.String('us-zip-area/zip-pattern'), required=False) # regex: [a-zA-Z0-9_]+\*? Note the optional asterisk country_areas = gxml.List('', gxml.String('us-country-area/country-area'), values=('CONTINENTAL_48', 'FULL_50_STATES', 'ALL'), required=False) # enum('CONTINENTAL_48', 'FULL_50_STATES', 'ALL') class allowed_areas_t(areas_t): postal_areas = gxml.List('', gxml.Complex('postal-area', postal_area_t), required=False) world_area = PresentOrNot('world-area', required=False) class excluded_areas_t(areas_t): postal_areas = gxml.List('', gxml.Complex('postal-area', postal_area_t), required=False) class tax_rule_t(gxml.Node): rate = gxml.Double('rate', default=0.) tax_area = gxml.Complex('tax-area', tax_area_t, required=False) tax_areas = gxml.List('tax-areas', gxml.Complex('', tax_area_t), required=False) class default_tax_rule_t(tax_rule_t): shipping_taxed = gxml.Boolean('shipping-taxed', required=False) class alternate_tax_rule_t(tax_rule_t): pass class default_tax_table_t(gxml.Node): tax_rules = gxml.List('tax-rules', gxml.Complex('default-tax-rule', default_tax_rule_t)) class alternate_tax_table_t(gxml.Node): name = gxml.String('@name') standalone = gxml.Boolean('@standalone') alternate_tax_rules = gxml.List('alternate-tax-rules', gxml.Complex('alternate-tax-rule', alternate_tax_rule_t)) class tax_tables_t(gxml.Node): merchant_calculated = gxml.Boolean('@merchant-calculated', default=False) default = gxml.Complex('default-tax-table', default_tax_table_t) alternates = gxml.List('alternate-tax-tables', gxml.Complex('alternate-tax-table', alternate_tax_table_t), required=False) class merchant_calculations_t(gxml.Node): merchant_calculations_url = gxml.Url('merchant-calculations-url') accept_merchant_coupons = gxml.Boolean('accept-merchant-coupons', required=False) accept_gift_certificates = gxml.Boolean('accept-gift-certificates', required=False) class shipping_option_t(gxml.Node): """ Represents information about shipping costs. >>> test_node( ... shipping_option_t( ... name = 'Testing', ... price = price_t( ... currency = 'GBP', ... value = 9.99, ... ), ... allowed_areas = allowed_areas_t( ... world_area = True, ... ), ... excluded_areas = excluded_areas_t( ... postal_areas = [postal_area_t( ... country_code = 'US', ... )], ... ), ... ) ... , ''' ... ... 9.990 ... ... ... ... ... ... ... US ... ... ... ... ... ''') """ name = gxml.String('@name') # Attribute, len <= 255, not-empty price = gxml.Complex('price', price_t) allowed_areas = gxml.Complex('shipping-restrictions/allowed-areas', allowed_areas_t, required=False) excluded_areas = gxml.Complex('shipping-restrictions/excluded-areas', excluded_areas_t, required=False) class address_filters_t(gxml.Node): allowed_areas = gxml.Complex('allowed-areas', allowed_areas_t, required=False) excluded_areas = gxml.Complex('excluded-areas', excluded_areas_t, required=False) allow_us_po_box = gxml.Boolean('allow-us-po-box', required=False) class flat_rate_shipping_t(shipping_option_t): pass class merchant_calculated_shipping_t(shipping_option_t): address_filters = gxml.Complex('address-filters', address_filters_t, required=False) # TODO: Add some special type of validation to: # shipping_company and shipping_type fields. See: # http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Tag_Reference.html#tag_shipping-type class carrier_calculated_shipping_option_t(gxml.Node): price = gxml.Complex('price', price_t) shipping_company = gxml.String('shipping-company', values=('FedEx', 'UPS', 'USPS')) carrier_pickup = gxml.String('carrier-pickup', values=('REGULAR_PICKUP', 'SPECIAL_PICKUP', 'DROP_OFF'), default='DROP_OFF', required=False) shipping_type = gxml.String('shipping-type') additional_fixed_charge = gxml.Complex('additional-fixed-charge', price_t, required=False) additional_variable_charge_percent = gxml.Double('additional-variable-charge-percent', required=False) class measure_t(gxml.Node): unit = gxml.String('@unit', values=('IN',), default='IN') value = gxml.Decimal('@value') class ship_from_t(gxml.Node): id = gxml.String('id') city = gxml.String('city') region = gxml.String('region', empty=True) postal_code = gxml.Zip('postal-code') country_code = CountryCode('country-code') class shipping_package_t(gxml.Node): delivery_address_category = gxml.String('delivery-address-category', values=('RESIDENTIAL', 'COMMERCIAL'), required=False) height = gxml.Complex('height', measure_t) length = gxml.Complex('length', measure_t) ship_from = gxml.Complex('ship-from', ship_from_t) width = gxml.Complex('width', measure_t) class carrier_calculated_shipping_t(gxml.Node): carrier_calculated_shipping_options = gxml.List('carrier-calculated-shipping-options', gxml.Complex('carrier-calculated-shipping-option', carrier_calculated_shipping_option_t)) shipping_packages = gxml.List('shipping-packages', gxml.Complex('shipping-package', shipping_package_t)) class pickup_t(gxml.Node): name = gxml.String('@name') price = gxml.Complex('price', price_t) class shipping_methods_t(gxml.Node): carrier_calculated_shippings = gxml.List('', gxml.Complex('carrier-calculated-shipping', carrier_calculated_shipping_t), required=False) # list of carrier_calculated_shipping_t flat_rate_shippings = gxml.List('', gxml.Complex('flat-rate-shipping', flat_rate_shipping_t), required=False) # list of flat_rate_shipping_t merchant_calculated_shippings = gxml.List('', gxml.Complex('merchant-calculated-shipping', merchant_calculated_shipping_t), required=False) # list of merchant_calculated_shipping_t pickups = gxml.List('', gxml.Complex('pickup', pickup_t), required=False) # list of pickup_t URL_PARAMETER_TYPES=( 'buyer-id', # A Google-assigned value that uniquely identifies a customer email address. '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. 'order-subtotal', # The total cost for all of the items in the order including coupons and discounts but excluding taxes and shipping charges. 'order-subtotal-plus-tax', # The total cost for all of the items in the order, including taxes, coupons and discounts, but excluding shipping charges. 'order-subtotal-plus-shipping', # The total cost for all of the items in the order, including shipping charges, coupons and discounts, but excluding taxes. 'order-total', # The total cost for all of the items in the order, including taxes, shipping charges, coupons and discounts. 'tax-amount', # The total amount of taxes charged for an order. 'shipping-amount', # The shipping cost associated with an order. 'coupon-amount', # The total amount of all coupons factored into the order total. 'billing-city', # The city associated with the order's billing address. 'billing-region', # The U.S. state associated with the order's billing address. 'billing-postal-code', # The five-digit U.S. zip code associated with the order's billing address. 'billing-country-code', # The two-letter ISO 3166 country code associated with the order's billing address. 'shipping-city', # The city associated with the order's shipping address. 'shipping-region', # The U.S. state associated with the order's shipping address. 'shipping-postal-code', # The five-digit U.S. zip code associated with the order's shipping address. 'shipping-country-code', # The two-letter ISO 3166 country code associated with the order's shipping address.', ) class url_parameter_t(gxml.Node): name = gxml.String('@name') type = gxml.String('@type', values=URL_PARAMETER_TYPES) class parameterized_url_t(gxml.Node): url = gxml.Url('@url', required=True) parameters = gxml.List('parameters', gxml.Complex('url-parameter', url_parameter_t), required=True) class rounding_policy_t(gxml.Node): mode = gxml.String('mode', choices=('UP', 'DOWN', 'CEILING', 'FLOOR', 'HALF_UP', 'HALF_DOWN', 'HALF_EVEN', 'UNNECESSARY')) rule = gxml.String('rule', choices=('PER_LINE', 'TOTAL')) class checkout_flow_support_t(gxml.Node): """ >>> test_node( ... checkout_flow_support_t( ... parameterized_urls = [ ... parameterized_url_t( ... url='http://google.com/', ... parameters=[url_parameter_t(name='a', type='buyer-id')] ... ), ... parameterized_url_t( ... url='http://yahoo.com/', ... parameters=[url_parameter_t(name='a', type='shipping-city'), ... url_parameter_t(name='b', type='tax-amount')] ... ), ... parameterized_url_t( ... url='http://mozilla.com/', ... parameters=[url_parameter_t(name='a', type='order-total'), ... url_parameter_t(name='b', type='shipping-region'), ... url_parameter_t(name='c', type='shipping-country-code')] ... ) ... ], ... ) ... , ... ''' ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ''' ... ) """ edit_cart_url = gxml.Url('edit-cart-url', required=False) # optional, URL continue_shopping_url = gxml.Url('continue-shopping-url', required=False) # optional, URL tax_tables = gxml.Complex('tax-tables', tax_tables_t, required=False) # optional, tax_tables_t shipping_methods = gxml.Complex('shipping-methods', shipping_methods_t, required=False) # optional, shipping_methods_t parameterized_urls = gxml.List('parameterized-urls', gxml.Complex('parameterized-url', parameterized_url_t), required=False) merchant_calculations = gxml.Complex('merchant-calculations', merchant_calculations_t, required=False) # optional, merchant_calculations_t request_buyer_phone_number = gxml.Boolean('request-buyer-phone-number', required=False) # optional, Boolean analytics_data = gxml.String('analytics-data', required=False) platform_id = gxml.Long('platform-id', required=False) rounding_policy = gxml.Complex('rounding-policy', rounding_policy_t, required=False) class shopping_cart_t(gxml.Node): expiration = gxml.Timestamp('cart-expiration/good-until-date', required=False) items = gxml.List('items', gxml.Complex('item', item_t)) merchant_private_data = gxml.Any('merchant-private-data', save_node_and_xml=True, required=False) class hello_t(gxml.Document): """ Represents a simple test that verifies that your server communicates properly with Google Checkout. The fourth step of the U{Getting Started with Google Checkout} section of the Developer's Guide explains how to execute this test. >>> test_document(hello_t(), ... "" ... ) """ tag_name='hello' class bye_t(gxml.Document): """ Represents a response that indicates that Google correctly received a request. >>> test_document( ... bye_t(serial_number="7315dacf-3a2e-80d5-aa36-8345cb54c143") ... , ... ''' ... ... ''' ... ) """ tag_name = 'bye' serial_number = gxml.ID('@serial-number') class checkout_shopping_cart_t(gxml.Document): tag_name = 'checkout-shopping-cart' shopping_cart = gxml.Complex('shopping-cart', shopping_cart_t) checkout_flow_support = gxml.Complex('checkout-flow-support/merchant-checkout-flow-support', checkout_flow_support_t) request_initial_auth_details = gxml.Boolean('order-processing-support/request-initial-auth-details', required=False) class coupon_gift_adjustment_t(gxml.Node): code = gxml.String('code') calculated_amount = gxml.Complex('calculated-amount', price_t, required=False) applied_amount = gxml.Complex('applied-amount', price_t) message = gxml.String('message', required=False) class merchant_codes_t(gxml.Node): gift_certificate_adjustment = gxml.List('', gxml.Complex('gift-certificate-adjustment', coupon_gift_adjustment_t)) coupon_adjustment = gxml.List('', gxml.Complex('coupon-adjustment', coupon_gift_adjustment_t)) class shipping_adjustment_t(gxml.Node): shipping_name = gxml.String('shipping-name') shipping_cost = gxml.Complex('shipping-cost', price_t) class carrier_calculated_shipping_adjustment_t(gxml.Node): shipping_name = gxml.String('shipping-name') shipping_cost = gxml.Complex('shipping-cost', price_t) # Two classes below represent the single 'shipping' tag, which content # depends on the context the XML Node is present. # http://code.google.com/apis/checkout/developer/index.html#tag_shipping class shipping_in_order_adjustment_t(gxml.Node): carrier_calculated_shipping_adjustment = gxml.Complex('carrier-calculated-shipping-adjustment', carrier_calculated_shipping_adjustment_t, required=False) flat_rate_shipping_adjustment = gxml.Complex('flat-rate-shipping-adjustment', shipping_adjustment_t, required=False) merchant_calculated_shipping_adjustment = gxml.Complex('merchant-calculated-shipping-adjustment', shipping_adjustment_t, required=False) pickup_shipping_adjustment = gxml.Complex('pickup-shipping-adjustment', shipping_adjustment_t, required=False) methods = gxml.List('', gxml.String('method/@name')) class shipping_in_calculate_t(gxml.Node): methods = gxml.List('', gxml.String('method/@name')) class order_adjustment_t(gxml.Node): adjustment_total = gxml.Complex('adjustment-total', price_t, required=False) merchant_calculation_successful = gxml.Boolean('merchant-calculation-successful', required=False) merchant_codes = gxml.Complex('merchant-codes', merchant_codes_t, required=False) shipping = gxml.Complex('shipping', shipping_in_order_adjustment_t, required=False) total_tax = gxml.Complex('total-tax', price_t, required=False) class structured_name_t(gxml.Node): first_name = gxml.String('first-name') last_name = gxml.String('last-name') class address_t(gxml.Node): address1 = gxml.String('address1') address2 = gxml.String('address2', required=False) city = gxml.String('city') company_name = gxml.String('company-name', required=False) contact_name = gxml.String('contact-name', required=False) country_code = gxml.String('country-code') email = gxml.Email('email', required=False) fax = gxml.Phone('fax', required=False, empty=True) phone = gxml.Phone('phone', required=False, empty=True) postal_code = gxml.Zip('postal-code') region = gxml.String('region', empty=True) structured_name = gxml.Complex('structured-name', structured_name_t, required=False) class buyer_billing_address_t(address_t): pass class buyer_shipping_address_t(address_t): pass class billing_address_t(address_t): # google docs do not say address_id is optional, but sandbox omits it.. :S bug? # address_id = gxml.ID('@address-id', required=False) pass class buyer_marketing_preferences_t(gxml.Node): email_allowed = gxml.Boolean('email-allowed') def read(self, node): return gxml.Node.read(self, node) class abstract_notification_t(gxml.Document): tag_name = '-notification' serial_number = gxml.ID('@serial-number') google_order_number = gxml.ID('google-order-number') timestamp = gxml.Timestamp('timestamp') class promotion_t(gxml.Node): description = gxml.String('description', required=False, max_length=1024) id = gxml.Long('id') name = gxml.String('name') total_amount = gxml.Complex('total-amount', price_t) FINANCIAL_ORDER_STATE=('REVIEWING', 'CHARGEABLE', 'CHARGING', 'CHARGED', 'PAYMENT_DECLINED', 'CANCELLED', 'CANCELLED_BY_GOOGLE') FULFILLMENT_ORDER_STATE=('NEW', 'PROCESSING', 'DELIVERED', 'WILL_NOT_DELIVER') class new_order_notification_t(abstract_notification_t): tag_name = 'new-order-notification' buyer_billing_address = gxml.Complex('buyer-billing-address', buyer_billing_address_t) buyer_id = gxml.Long('buyer-id') buyer_marketing_preferences = gxml.Complex('buyer-marketing-preferences', buyer_marketing_preferences_t) buyer_shipping_address = gxml.Complex('buyer-shipping-address', buyer_shipping_address_t) financial_order_state = gxml.String('financial-order-state', values=FINANCIAL_ORDER_STATE) fulfillment_order_state = gxml.String('fulfillment-order-state', values=FULFILLMENT_ORDER_STATE) order_adjustment = gxml.Complex('order-adjustment', order_adjustment_t) order_total = gxml.Complex('order-total', price_t) shopping_cart = gxml.Complex('shopping-cart', shopping_cart_t) promotions = gxml.List('promotions', gxml.Complex('promotion', promotion_t), required=False) class checkout_redirect_t(gxml.Document): """ Try doctests: >>> a = checkout_redirect_t(serial_number='blabla12345', ... redirect_url='http://www.somewhere.com') >>> b = gxml.Document.fromxml(a.toxml()) >>> a == b True """ tag_name = 'checkout-redirect' serial_number = gxml.ID('@serial-number') redirect_url = gxml.Url('redirect-url') class notification_acknowledgment_t(gxml.Document): tag_name = 'notification-acknowledgment' serial_number = gxml.ID('@serial-number') class order_state_change_notification_t(abstract_notification_t): tag_name = 'order-state-change-notification' new_fulfillment_order_state = gxml.String('new-fulfillment-order-state', values=FINANCIAL_ORDER_STATE) new_financial_order_state = gxml.String('new-financial-order-state', values=FULFILLMENT_ORDER_STATE) previous_financial_order_state = gxml.String('previous-financial-order-state', values=FINANCIAL_ORDER_STATE) previous_fulfillment_order_state = gxml.String('previous-fulfillment-order-state', values=FULFILLMENT_ORDER_STATE) reason = gxml.String('reason', required=False) AVS_VALUES=('Y', 'P', 'A', 'N', 'U') CVN_VALUES=('M', 'N', 'U', 'E') class risk_information_t(gxml.Node): avs_response = gxml.String('avs-response', values=AVS_VALUES) billing_address = gxml.Complex('billing-address', billing_address_t) buyer_account_age = gxml.Integer('buyer-account-age') cvn_response = gxml.String('cvn-response', values=CVN_VALUES) eligible_for_protection = gxml.Boolean('eligible-for-protection') ip_address = gxml.IP('ip-address') partial_cc_number = gxml.String('partial-cc-number') # partial CC Number class risk_information_notification_t(abstract_notification_t): tag_name = 'risk-information-notification' risk_information = gxml.Complex('risk-information', risk_information_t) class abstract_order_t(gxml.Document): tag_name='-order' google_order_number = gxml.ID('@google-order-number') class charge_order_t(abstract_order_t): tag_name = 'charge-order' amount = gxml.Complex('amount', price_t, required=False) class refund_order_t(abstract_order_t): tag_name = 'refund-order' amount = gxml.Complex('amount', price_t, required=False) comment = gxml.String('comment', max_length=140, required=False) reason = gxml.String('reason', max_length=140) class cancel_order_t(abstract_order_t): """ Represents an order that should be canceled. A command sets the financial-order-state and the fulfillment-order-state to canceled. >>> test_document( ... cancel_order_t(google_order_number = "841171949013218", ... comment = 'Buyer found a better deal.', ... reason = 'Buyer cancelled the order.' ... ) ... , ... ''' ... ... Buyer found a better deal. ... Buyer cancelled the order. ... ... ''' ... ) """ tag_name = 'cancel-order' comment = gxml.String('comment', max_length=140, required=False) reason = gxml.String('reason', max_length=140) class authorize_order_t(abstract_order_t): tag_name = 'authorize-order' class process_order_t(abstract_order_t): tag_name = 'process-order' class add_merchant_order_number_t(abstract_order_t): tag_name = 'add-merchant-order-number' merchant_order_number = gxml.String('merchant-order-number') CARRIER_VALUES=('DHL', 'FedEx', 'UPS', 'USPS', 'Other') class tracking_data_t(gxml.Node): carrier = gxml.String('carrier', values=CARRIER_VALUES) tracking_number = gxml.String('tracking-number') class deliver_order_t(abstract_order_t): tag_name='deliver-order' tracking_data = gxml.Complex('tracking-data', tracking_data_t, required=False) send_email = gxml.Boolean('send-email', required=False) class add_tracking_data_t(abstract_order_t): """ Represents a tag containing a request to add a shipper's tracking number to an order. >>> test_document( ... add_tracking_data_t( ... google_order_number = '841171949013218', ... tracking_data = tracking_data_t( ... carrier = 'UPS', ... tracking_number = 'Z9842W69871281267' ... ) ... ) ... , ... ''' ... ... ... Z9842W69871281267 ... UPS ... ... ... ''' ... ) """ tag_name='add-tracking-data' tracking_data = gxml.Complex('tracking-data', tracking_data_t) class send_buyer_message_t(abstract_order_t): tag_name='send-buyer-message' send_email = gxml.Boolean('send-email', required=False) message = gxml.String('message') class archive_order_t(abstract_order_t): """ Represents a request to archive a particular order. You would archive an order to remove it from your Merchant Center Inbox, indicating that the order has been delivered. >>> test_document(archive_order_t(google_order_number = '841171949013218'), ... '''''' ... ) """ tag_name='archive-order' class unarchive_order_t(abstract_order_t): tag_name='unarchive-order' class charge_amount_notification_t(abstract_notification_t): """ Represents information about a successful charge for an order. >>> from datetime import datetime >>> import iso8601 >>> test_document( ... charge_amount_notification_t( ... serial_number='95d44287-12b1-4722-bc56-cfaa73f4c0d1', ... google_order_number = '841171949013218', ... timestamp = iso8601.parse_date('2006-03-18T18:25:31.593Z'), ... latest_charge_amount = price_t(currency='USD', value=2226.06), ... total_charge_amount = price_t(currency='USD', value=2226.06) ... ) ... , ... ''' ... ... 2226.060 ... 841171949013218 ... 2226.060 ... 2006-03-18T18:25:31.593000+00:00 ... ... ''' ... ) """ tag_name='charge-amount-notification' latest_charge_amount = gxml.Complex('latest-charge-amount', price_t) latest_promotion_charge_amount = gxml.Complex('latest-promotion-charge-amount', price_t, required=False) total_charge_amount = gxml.Complex('total-charge-amount', price_t) class refund_amount_notification_t(abstract_notification_t): tag_name='refund-amount-notification' latest_refund_amount = gxml.Complex('latest-refund-amount', price_t) latest_promotion_refund_amount = gxml.Complex('latest-promotion-refund-amount', price_t, required=False) total_refund_amount = gxml.Complex('total-refund-amount', price_t) class chargeback_amount_notification_t(abstract_notification_t): tag_name='chargeback-amount-notification' latest_chargeback_amount = gxml.Complex('latest-chargeback-amount', price_t) latest_promotion_chargeback_amount = gxml.Complex('latest-promotion-chargeback-amount', price_t, required=False) total_chargeback_amount = gxml.Complex('total-chargeback-amount', price_t) class authorization_amount_notification_t(abstract_notification_t): tag_name='authorization-amount-notification' authorization_amount = gxml.Complex('authorization-amount', price_t) authorization_expiration_date = gxml.Timestamp('authorization-expiration-date') avs_response = gxml.String('avs-response', values=AVS_VALUES) cvn_response = gxml.String('cvn-response', values=CVN_VALUES) class anonymous_address_t(gxml.Node): id = gxml.String('@id') city = gxml.String('city') region = gxml.String('region', empty=True) postal_code = gxml.Zip('postal-code') country_code = gxml.String('country-code') class merchant_code_string_t(gxml.Node): code = gxml.String('@code') class calculate_t(gxml.Node): addresses = gxml.List('addresses', gxml.Complex('anonymous-address', anonymous_address_t), required=False) merchant_code_strings = gxml.Complex('merchant-code-strings/merchant-code-string', merchant_code_string_t, required=False) shipping = gxml.Complex('shipping', shipping_in_calculate_t, required=False) tax = gxml.Boolean('tax') class merchant_calculation_callback_t(gxml.Document): tag_name = 'merchant-calculation-callback' serial_number = gxml.ID('@serial-number') buyer_id = gxml.Long('buyer-id', required=False) buyer_language = gxml.LanguageCode('buyer-language') calculate = gxml.Complex('calculate', calculate_t) shopping_cart = gxml.Complex('shopping-cart', shopping_cart_t) class discount_result_t(gxml.Node): valid = gxml.Boolean('valid') calculated_amount = gxml.Complex('calculated-amount', price_t) code = gxml.String('code') message = gxml.String('message', max_length=255) class merchant_code_results_t(gxml.Node): coupon_result = gxml.List('', gxml.Complex('coupon-result', discount_result_t)) gift_certificate_result = gxml.List('', gxml.Complex('gift-certificate-result', discount_result_t)) class result_t(gxml.Node): shipping_name = gxml.String('@shipping-name') address_id = gxml.String('@address-id') total_tax = gxml.Complex('total-tax', price_t, required=False) shipping_rate = gxml.Complex('shipping-rate', price_t, required=False) shippable = gxml.Boolean('shippable', required=False) merchant_code_results = gxml.Complex('merchant-code-results', merchant_code_results_t, required=False) class merchant_calculation_results_t(gxml.Document): tag_name = 'merchant-calculation-results' results = gxml.List('results', gxml.Complex('result', result_t)) class request_received_t(gxml.Document): tag_name = 'request-received' serial_number = gxml.ID('@serial-number') # This is custom message type which is only suitable for returning to google # the 'Ok' response. class ok_t(gxml.Document): tag_name = 'ok' class error_t(gxml.Document): """ Represents a response containing information about an invalid API request. The information is intended to help you debug the problem causing the error. >>> test_document( ... error_t(serial_number = '3c394432-8270-411b-9239-98c2c499f87f', ... error_message='Bad username and/or password for API Access.', ... warning_messages = ['IP address is suspicious.', ... 'MAC address is shadowed.'] ... ) ... , ... ''' ... ... Bad username and/or password for API Access. ... ... IP address is suspicious. ... MAC address is shadowed. ... ... ... ''' ... ) """ tag_name = 'error' serial_number = gxml.ID('@serial-number') error_message = gxml.String('error-message') warning_messages = gxml.List('warning-messages', gxml.String('string'), required=False) class diagnosis_t(gxml.Document): """ Represents a diagnostic response to an API request. The diagnostic response contains the parsed XML in your request as well as any warnings generated by your request. Please see the U{Validating XML Messages to Google Checkout } section for more information about diagnostic requests and responses. """ tag_name = 'diagnosis' input_xml = gxml.Any('input-xml') warnings = gxml.List('warnings', gxml.String('string'), required=False) serial_number = gxml.ID('@serial-number') class demo_failure_t(gxml.Document): """ >>> test_document( ... demo_failure_t(message='Demo Failure Message') ... , ... '''''' ... ) """ tag_name = 'demo-failure' message = gxml.String('@message', max_length=25) class item_id_t(gxml.Node): merchant_item_id = gxml.String('merchant-item-id') class backorder_items_t(abstract_order_t): tag_name = 'backorder-items' item_ids = gxml.List('item-ids', gxml.Complex('item-id', item_id_t)) send_email = gxml.Boolean('send-email', required=False) class cancel_items_t(abstract_order_t): tag_name = 'cancel-items' reason = gxml.String('comment') comment = gxml.String('comment') item_ids = gxml.List('item-ids', gxml.Complex('item-id', item_id_t)) send_email = gxml.Boolean('send-email', required=False) class reset_items_shipping_information_t(abstract_order_t): tag_name = 'reset-items-shipping-information' item_ids = gxml.List('item-ids', gxml.Complex('item-id', item_id_t)) send_email = gxml.Boolean('send-email', required=False) class return_items_t(abstract_order_t): tag_name = 'return-items' item_ids = gxml.List('item-ids', gxml.Complex('item-id', item_id_t)) send_email = gxml.Boolean('send-email', required=False) class item_shipping_information_t(gxml.Node): item_id = gxml.Complex('item-id', item_id_t) tracking_data_list = gxml.List('tracking-data-list', gxml.Complex('tracking-data', tracking_data_t)) class ship_items_t(abstract_order_t): tag_name = 'ship-items' item_shipping_information_list = gxml.List('item-shipping-information-list', gxml.Complex('item-shipping-information', item_shipping_information_t)) send_email = gxml.Boolean('send-email') if __name__ == "__main__": def run_doctests(): import doctest doctest.testmod() run_doctests()