Class: Google4R::Checkout::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/google4r/checkout/commands.rb

Overview

Abstract super class for all commands that are to be sent to Google. Provides the base functionality for signing and encoding the cart.

Constant Summary collapse

SANDBOX_URL_PREFIX =

The URL to use for requests to the sandboxed API. The merchant id is to be put in via String#%.

'https://sandbox.google.com/checkout/'
PRODUCTION_URL_PREFIX =

The URL to use for real requests to the Google Checkout API. The merchant id is to be put in via String#%.

'https://checkout.google.com/'
CHECKOUT_API_URL =

Orders

'api/checkout/v2/merchantCheckout/Merchant/%s'
ORDER_PROCESSING_API_URL =
'api/checkout/v2/request/Merchant/%s'
ORDER_REPORT_API_URL =
'api/checkout/v2/reports/Merchant/%s'
POLLING_API_URL =
'api/checkout/v2/reports/Merchant/%s'
'api/checkout/v2/merchantCheckout/Donations/%s'
'api/checkout/v2/request/Donations/%s'
'api/checkout/v2/reports/Donations/%s'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(frontend) ⇒ Command

Initialize the frontend attribute with the value of the frontend parameter.



72
73
74
75
76
77
# File 'lib/google4r/checkout/commands.rb', line 72

def initialize(frontend)
  if self.instance_of?(Command) || self.instance_of?(ItemsCommand)
    raise 'Cannot instantiate abstract class ' + self.class.to_s
  end
  @frontend = frontend
end

Instance Attribute Details

#command_tag_nameObject (readonly)

The tag name of the command



66
67
68
# File 'lib/google4r/checkout/commands.rb', line 66

def command_tag_name
  @command_tag_name
end

#frontendObject (readonly)

The Frontent class that was used to create this CheckoutCommand and whose configuration will be used.



63
64
65
# File 'lib/google4r/checkout/commands.rb', line 63

def frontend
  @frontend
end

#google_order_numberObject

The google order number, required, String



69
70
71
# File 'lib/google4r/checkout/commands.rb', line 69

def google_order_number
  @google_order_number
end

Instance Method Details

#send_to_google_checkoutObject

Sends the cart’s XML to GoogleCheckout via HTTPs with Basic Auth.

Raises an OpenSSL::SSL::SSLError when the SSL certificate verification failed.

Raises a GoogleCheckoutError when Google returns an error.

Raises a RuntimeException on unknown responses. – TODO: The send-and-expect-response part should be adaptable to other commands and responses. ++



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/google4r/checkout/commands.rb', line 89

def send_to_google_checkout
  xml_response = (self.class == OrderReportCommand) ? false : true        
  # Create HTTP(S) POST command and set up Basic Authentication.
  url_str = 
    if frontend.configuration[:use_sandbox] then
      SANDBOX_URL_PREFIX
    else
      PRODUCTION_URL_PREFIX
    end
  url_str += 
    if frontend.configuration[:purchase_type] == :donation
      if self.class == CheckoutCommand then
        DONATE_CHECKOUT_API_URL
      elsif self.class == OrderReportCommand || self.class == NotificationHistoryRequestCommand then
        DONATE_ORDER_REPORT_API_URL
      else
        DONATE_ORDER_PROCESSING_API_URL
      end
    else
      if self.class == CheckoutCommand then
        CHECKOUT_API_URL
      elsif self.class == OrderReportCommand || self.class == NotificationHistoryRequestCommand then
        ORDER_REPORT_API_URL
      elsif self.class == NotificationDataRequestCommand || self.class == NotificationDataTokenRequestCommand then
        POLLING_API_URL
      else
        ORDER_PROCESSING_API_URL
      end
    end
  url_str = url_str % frontend.configuration[:merchant_id]

  url = URI.parse(url_str)
  
  request = Net::HTTP::Post.new(url.path)
  request.basic_auth(frontend.configuration[:merchant_id], frontend.configuration[:merchant_key])

  # Set up the HTTP connection object and the SSL layer.
  https = Net::HTTP.new(url.host, url.port)
  https.use_ssl = true
  https.cert_store = self.class.x509_store
  https.verify_mode = OpenSSL::SSL::VERIFY_PEER
  https.verify_depth = 5
  https.verify_callback = Proc.new do |ok, ctx|
    domain = ctx.chain.first.subject.to_a.select { |part| part.first == "CN" }.first[1]
    
    domain == if frontend.configuration[:use_sandbox]
      "sandbox.google.com"
    else
      "checkout.google.com"
    end
  end

  # Send the request to Google.
  result = https.request(request, self.to_xml)
  
  case result
  when Net::HTTPSuccess then
    if ( xml_response ) then
      xml_doc = REXML::Document.new(result.body)
       
      case xml_doc.root.name
      when 'checkout-redirect'
          serial_number = xml_doc.elements['/checkout-redirect'].attributes['serial-number']
          redirect_url = xml_doc.elements['/checkout-redirect/redirect-url/text()'].value
          return CheckoutRedirectResponse.new(serial_number, redirect_url)
      when 'request-received'
          serial_number = xml_doc.elements['/request-received'].attributes['serial-number']
          return serial_number
      # report history notifications
      when 'order-summary'
          raise 'Response type OrderSummaryResponse not implemented'
      when 'new-order-notification'
          return NewOrderNotification.create_from_element xml_doc.root, @frontend
      when 'risk-information-notification'
          return RiskInformationNotification.create_from_element xml_doc.root, @frontend
      when 'order-state-change-notification'
          return OrderStateChangeNotification.create_from_element xml_doc.root, @frontend
      when 'charge-amount-notification'
          return ChargeAmountNotification.create_from_element xml_doc.root, @frontend
      when 'authorization-amount-notification'
          return AuthorizationAmountNotification.create_from_element xml_doc.root, @frontend
      when 'refund-amount-notification'
          return RefundAmountNotification.create_from_element xml_doc.root, @frontend
      when 'chargeback-amount-notification'
          return ChargebackAmountNotification.create_from_element xml_doc.root, @frontend
      when 'notification-history-response'
          next_page_token = xml_doc.root.elements['next-page-token'].try(:value)
          notifications = xml_doc.root.elements['notifications'].elements.map do |element| 
            case element.name
              when 'new-order-notification'
                NewOrderNotification.create_from_element element, @frontend
              when 'risk-information-notification'
                RiskInformationNotification.create_from_element element, @frontend
              when 'order-state-change-notification'
                OrderStateChangeNotification.create_from_element element, @frontend
              when 'charge-amount-notification'
                ChargeAmountNotification.create_from_element element, @frontend
              when 'authorization-amount-notification'
                AuthorizationAmountNotification.create_from_element element, @frontend
              when 'refund-amount-notification'
                RefundAmountNotification.create_from_element element, @frontend
              when 'chargeback-amount-notification'
                ChargebackAmountNotification.create_from_element element, @frontend
            end
          end
          { :notifications => notifications, :next_page_token => next_page_token }
        when 'notification-data-token-response'
          serial_number = xml_doc.elements['/notification-data-token-response'].attributes['serial-number']
          continue_token = xml_doc.root.elements['continue-token/text()'].value
          { :continue_token => continue_token, :serial_number => serial_number}
        when 'notification-data-response'
          serial_number = xml_doc.elements['/notification-data-response'].attributes['serial-number']
          continue_token = xml_doc.root.elements['continue-token/text()'].value
          has_more_notifications = xml_doc.root.elements['has-more-notifications/text()'].value
          notifications = xml_doc.root.elements['notifications'].elements.map do |element|
            case element.name
              when 'new-order-notification'
                NewOrderNotification.create_from_element element, @frontend
              when 'risk-information-notification'
                RiskInformationNotification.create_from_element element, @frontend
              when 'order-state-change-notification'
                OrderStateChangeNotification.create_from_element element, @frontend
              when 'charge-amount-notification'
                ChargeAmountNotification.create_from_element element, @frontend
              when 'authorization-amount-notification'
                AuthorizationAmountNotification.create_from_element element, @frontend
              when 'refund-amount-notification'
                RefundAmountNotification.create_from_element element, @frontend
              when 'chargeback-amount-notification'
                ChargebackAmountNotification.create_from_element element, @frontend
            end
          end
          { :notifications => notifications, :continue_token => continue_token, :serial_number => serial_number, :has_more_notifications => has_more_notifications }
      else
          raise "Unknown response:\n--\n#{xml_doc.to_s}\n--"
      end
    else
      # handle the CSV output of the order-report-list command
      return result.body
    end
  when Net::HTTPClientError then
    xml_doc = REXML::Document.new(result.body)
    
    if xml_doc.elements['/error'].attributes['serial-number'].nil? or xml_doc.elements['/error/error-message/text()'].nil? then
      raise "Invalid response from Google:\n---\n#{result.body}\n---"
    end
    
    hash = 
      {
        :serial_number => xml_doc.elements['/error'].attributes['serial-number'],
        :message       => xml_doc.elements['/error/error-message/text()'].value
      }
    
    raise GoogleCheckoutError.new(hash)
  when Net::HTTPRedirection, Net::HTTPServerError, Net::HTTPInformation then
    raise "Unexpected reponse code (#{result.class}): #{result.code} - #{result.message}"
  else
    raise "Unknown reponse code: #{result.code} - #{result.message}"
  end
end

#to_xmlObject

Class method to return the command’s XML representation.



251
252
253
254
# File 'lib/google4r/checkout/commands.rb', line 251

def to_xml
  generator_class = Google4R::Command.get_const("#{self.class}XmlGenerator")
  return generator_class.new(self).generate
end