Class: ActiveMerchant::Shipping::USPS

Inherits:
Carrier show all
Defined in:
lib/active_shipping/shipping/carriers/usps.rb

Overview

After getting an API login from USPS (looks like '123YOURNAME456'), run the following test:

usps = USPS.new(:login => '123YOURNAME456', :test => true) usps.valid_credentials?

This will send a test request to the USPS test servers, which they ask you to do before they put your API key in production mode.

Defined Under Namespace

Classes: EventDetails

Constant Summary collapse

EVENT_MESSAGE_PATTERNS =
[
  /^(.*), (\w+ \d{1,2}, \d{4}, \d{1,2}:\d\d [ap]m), (.*), (\w\w) (\d{5})$/i,
  /^Your item \w{2,3} (out for delivery|delivered) at (\d{1,2}:\d\d [ap]m on \w+ \d{1,2}, \d{4}) in (.*), (\w\w) (\d{5})\.$/i
]
LIVE_DOMAIN =
'production.shippingapis.com'
LIVE_RESOURCE =
'ShippingAPI.dll'
TEST_DOMAINS =

indexed by security; e.g. TEST_DOMAINS[USE_SSL]

{ # indexed by security; e.g. TEST_DOMAINS[USE_SSL[:rates]]
  true => 'secure.shippingapis.com',
  false => 'testing.shippingapis.com'
}
TEST_RESOURCE =
'ShippingAPITest.dll'
API_CODES =
{
  :us_rates => 'RateV4',
  :world_rates => 'IntlRateV2',
  :test => 'CarrierPickupAvailability',
  :track => 'TrackV2'
}
USE_SSL =
{
  :us_rates => false,
  :world_rates => false,
  :test => true,
  :track => false
}
CONTAINERS =
{
  rectangular: 'RECTANGULAR',
  variable: 'VARIABLE',
  box: 'FLAT RATE BOX',
  box_large: 'LG FLAT RATE BOX',
  box_medium: 'MD FLAT RATE BOX',
  box_small: 'SM FLAT RATE BOX',
  envelope: 'FLAT RATE ENVELOPE',
  envelope_legal: 'LEGAL FLAT RATE ENVELOPE',
  envelope_padded: 'PADDED FLAT RATE ENVELOPE',
  envelope_gift_card: 'GIFT CARD FLAT RATE ENVELOPE',
  envelope_window: 'WINDOW FLAT RATE ENVELOPE',
  envelope_small: 'SM FLAT RATE ENVELOPE',
  package_service: 'PACKAGE SERVICE'
}
MAIL_TYPES =
{
  :package => 'Package',
  :postcard => 'Postcards or aerogrammes',
  :matter_for_the_blind => 'Matter for the blind',
  :envelope => 'Envelope'
}
PACKAGE_PROPERTIES =
{
  'ZipOrigination' => :origin_zip,
  'ZipDestination' => :destination_zip,
  'Pounds' => :pounds,
  'Ounces' => :ounces,
  'Container' => :container,
  'Size' => :size,
  'Machinable' => :machinable,
  'Zone' => :zone,
  'Postage' => :postage,
  'Restrictions' => :restrictions
}
POSTAGE_PROPERTIES =
{
  'MailService' => :service,
  'Rate' => :rate
}
US_SERVICES =
{
  :first_class => 'FIRST CLASS',
  :priority => 'PRIORITY',
  :express => 'EXPRESS',
  :bpm => 'BPM',
  :parcel => 'PARCEL',
  :media => 'MEDIA',
  :library => 'LIBRARY',
  :online => 'ONLINE',
  :plus => 'PLUS',
  :all => 'ALL'
}
DEFAULT_SERVICE =
Hash.new(:all).update(
  :base => :online,
  :plus => :plus
)
DOMESTIC_RATE_FIELD =
Hash.new('Rate').update(
  :base => 'CommercialRate',
  :plus => 'CommercialPlusRate'
)
INTERNATIONAL_RATE_FIELD =
Hash.new('Postage').update(
  :base => 'CommercialPostage',
  :plus => 'CommercialPlusPostage'
)
COMMERCIAL_FLAG_NAME =
{
  :base => 'CommercialFlag',
  :plus => 'CommercialPlusFlag'
}
FIRST_CLASS_MAIL_TYPES =
{
  :letter => 'LETTER',
  :flat => 'FLAT',
  :parcel => 'PARCEL',
  :post_card => 'POSTCARD',
  :package_service => 'PACKAGESERVICE'
}
US_POSSESSIONS =

Array of U.S. possessions according to USPS: www.usps.com/ship/official-abbreviations.htm

%w(AS FM GU MH MP PW PR VI)
COUNTRY_NAME_CONVERSIONS =

TODO: figure out how USPS likes to say “Ivory Coast”

Country names: pe.usps.gov/text/Imm/immctry.htm

{
  "BA" => "Bosnia-Herzegovina",
  "CD" => "Congo, Democratic Republic of the",
  "CG" => "Congo (Brazzaville),Republic of the",
  "CI" => "Côte d'Ivoire (Ivory Coast)",
  "CK" => "Cook Islands (New Zealand)",
  "FK" => "Falkland Islands",
  "GB" => "Great Britain and Northern Ireland",
  "GE" => "Georgia, Republic of",
  "IR" => "Iran",
  "KN" => "Saint Kitts (St. Christopher and Nevis)",
  "KP" => "North Korea (Korea, Democratic People's Republic of)",
  "KR" => "South Korea (Korea, Republic of)",
  "LA" => "Laos",
  "LY" => "Libya",
  "MC" => "Monaco (France)",
  "MD" => "Moldova",
  "MK" => "Macedonia, Republic of",
  "MM" => "Burma",
  "PN" => "Pitcairn Island",
  "RU" => "Russia",
  "SK" => "Slovak Republic",
  "TK" => "Tokelau (Union) Group (Western Samoa)",
  "TW" => "Taiwan",
  "TZ" => "Tanzania",
  "VA" => "Vatican City",
  "VG" => "British Virgin Islands",
  "VN" => "Vietnam",
  "WF" => "Wallis and Futuna Islands",
  "WS" => "Western Samoa"
}
STATUS_NODE_PATTERNS =
%w(
Error/Description
*/TrackInfo/Error/Description
RESPONSE_ERROR_MESSAGES =
[
  /There is no record of that mail item/,
  /This Information has not been included in this Test Server\./,
  /Delivery status information is not available/
]
ESCAPING_AND_SYMBOLS =
/<\S*>/
LEADING_USPS =
/^USPS/
TRAILING_ASTERISKS =
/\*+$/
SERVICE_NAME_SUBSTITUTIONS =
/#{ESCAPING_AND_SYMBOLS}|#{LEADING_USPS}|#{TRAILING_ASTERISKS}/
@@name =
"USPS"

Instance Attribute Summary

Attributes inherited from Carrier

#last_request, #test_mode

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Carrier

#create_shipment, #initialize

Constructor Details

This class inherits a constructor from ActiveMerchant::Shipping::Carrier

Class Method Details

.package_machinable?(package, options = {}) ⇒ Boolean

from info at www.usps.com/businessmail101/mailcharacteristics/parcels.htm

package.options – 25 lb. limit instead of 35 for books or other printed matter.

Defaults to false.

197
198
199
200
201
202
203
204
205
206
207
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 197

def self.package_machinable?(package, options = {})
  at_least_minimum =  package.inches(:length) >= 6.0 &&
                      package.inches(:width) >= 3.0 &&
                      package.inches(:height) >= 0.25 &&
                      package.ounces >= 6.0
  at_most_maximum  =  package.inches(:length) <= 34.0 &&
                      package.inches(:width) <= 17.0 &&
                      package.inches(:height) <= 17.0 &&
                      package.pounds <= (package.options[:books] ? 25.0 : 35.0)
  at_least_minimum && at_most_maximum
end

.size_code_for(package) ⇒ Object


185
186
187
188
189
190
191
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 185

def self.size_code_for(package)
  if package.inches(:max) <= 12
    'REGULAR'
  else
    'LARGE'
  end
end

Instance Method Details

#extract_event_details(message) ⇒ Object


237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 237

def extract_event_details(message)
  return EventDetails.new unless EVENT_MESSAGE_PATTERNS.any? { |pattern| message =~ pattern }
  description = $1.upcase
  timestamp = $2
  city = $3
  state = $4
  zip_code = $5

  time = Time.parse(timestamp)
  zoneless_time = Time.utc(time.year, time.month, time.mday, time.hour, time.min, time.sec)
  location = Location.new(city: city, state: state, postal_code: zip_code, country: 'USA')
  EventDetails.new(description, time, zoneless_time, location)
end

#find_rates(origin, destination, packages, options = {}) ⇒ Object


213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 213

def find_rates(origin, destination, packages, options = {})
  options = @options.merge(options)

  origin = Location.from(origin)
  destination = Location.from(destination)
  packages = Array(packages)

  domestic_codes = US_POSSESSIONS + ['US', nil]
  if domestic_codes.include?(destination.country_code(:alpha2))
    us_rates(origin, destination, packages, options)
  else
    world_rates(origin, destination, packages, options)
  end
end

#find_tracking_info(tracking_number, options = {}) ⇒ Object


178
179
180
181
182
183
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 178

def find_tracking_info(tracking_number, options = {})
  options = @options.update(options)
  tracking_request = build_tracking_request(tracking_number, options)
  response = commit(:track, tracking_request, (options[:test] || false))
  parse_tracking_response(response, options)
end

#maximum_weightObject


233
234
235
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 233

def maximum_weight
  Mass.new(70, :pounds)
end

#requirementsObject


209
210
211
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 209

def requirements
  [:login]
end

#valid_credentials?Boolean


228
229
230
231
# File 'lib/active_shipping/shipping/carriers/usps.rb', line 228

def valid_credentials?
  # Cannot test with find_rates because USPS doesn't allow that in test mode
  test_mode? ? canned_address_verification_works? : super
end