Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
require 'net/http'
require 'net/https'
require 'hpricot'
require 'ostruct'

module UPS
  class UPSError < StandardError
  end
  
  class Client
    def initialize(options)
      @prefs = options
      @logger = RAILS_DEFAULT_LOGGER
    end
    
    def rate(to_zip, weight)
      response = self.rate_request(@prefs['origin_zip'], to_zip, weight)
      Hpricot(response).at('//ratingserviceselectionresponse/ratedshipment/transportationcharges/monetaryvalue').inner_html.to_f
    end
    
    def rate_list(to_zip, weight, cost_overrides = {})
      response = self.rate_request(@prefs['origin_zip'], to_zip, weight, true)
      Hpricot(response).search('//ratedshipment').collect do |service| 
        OpenStruct.new(
          :service_code => service.at("service/code").inner_html,
          :label => Services[service.at("service/code").inner_html],
          :cost => cost_overrides[service.at("service/code").inner_html] ||  service.at("transportationcharges/monetaryvalue").inner_html.to_f)
      end.sort_by(&:cost)
    end
    
    Services = {
      '01' => 'UPS Next Day Air',
      '02' => 'UPS Second Day Air',
      '03' => 'UPS Ground',
      '07' => 'UPS Worldwide Express',
      '08' => 'UPS Worldwide Expedited', 
      '11' => 'UPS Standard',
      '12' => 'UPS Three-Day Select',
      '13' => 'UPS Next Day Air Saver', 
      '14' => 'UPS Next Day Air Early A.M.',
      '54' => 'UPS Worldwide Express Plus',
      '59' => 'UPS Second Day Air A.M.',
      '65' => 'UPS Saver'      
    }
    
    protected
    
      def xml_credentials
        xml = Builder::XmlMarkup.new :indent => 2
        xml.instruct!
        xml.AccessRequest do
          xml.AccessLicenseNumber @prefs["api_key"]
          xml.UserId @prefs["login"]
          xml.Password @prefs["password"]
        end
        xml.target!
      end
    
      def send_request(url, data)
        uri = URI.parse(@prefs["url"] + '/' + url)
        http = Net::HTTP.new(uri.host, uri.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
        @logger.info "Sending to UPS:\n #{xml_credentials + data}" if @prefs['debug']
        body = http.post(uri.request_uri, xml_credentials + data).body
        @logger.info "Received from UPS:\n #{body}" if @prefs['debug']
        if @error =  Hpricot(body).at('//ratingserviceselectionresponse/response/error/ErrorDescription').inner_html rescue nil
          raise UPSError, @error
        end
        body
      end
      
      def rate_request(from_zip, destination, weight, compare = false)
        xml = Builder::XmlMarkup.new :indent => 2
        xml.instruct!
        xml.RatingServiceSelectionRequest do
          xml.Request do
            xml.TransactionReference do
              xml.CustomerContext 'Rating and Service'
              xml.XpciVersion '1.0001'
            end
            xml.RequestAction 'Rate'
            xml.RequestOption 'shop' if compare
          end
          xml.PickupType do
            xml.Code '01'
          end
          xml.Shipment do
            xml.Shipper do
              xml.Address do
                xml.PostalCode from_zip
              end
            end
            xml.ShipTo do
              xml.Address do
                if destination.is_a?(Address)
                  xml.PostalCode destination.postal_code if destination.postal_code
                  xml.CountryCode destination.country || 'US'
                  xml.City destination.city if destination.city
                  xml.StateProvinceCode destination.state if destination.state
                else
                  xml.PostalCode destination
                end
              end
            end
            xml.Service do
              xml.Code '03'
            end
            xml.Package do
              xml.PackagingType do
                xml.Code '02'
                xml.Description 'Package'
              end
              xml.Description 'Rate Shopping'
              xml.PackageWeight do
                xml.Weight weight # in pounds
              end
            end
          end
        end
        self.send_request('Rate', xml.target!)
      end
  end
end