The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Net::Async::Webservice::UPS - UPS API client, non-blocking

VERSION

version 1.1.4

SYNOPSIS

 use IO::Async::Loop;
 use Net::Async::Webservice::UPS;

 my $loop = IO::Async::Loop->new;

 my $ups = Net::Async::Webservice::UPS->new({
   config_file => $ENV{HOME}.'/.naws_ups.conf',
   loop => $loop,
 });

 $ups->validate_address($postcode)->then(sub {
   my ($response) = @_;
   say $_->postal_code for @{$response->addresses};
   return Future->wrap();
 });

 $loop->run;

Alternatively:

 use Net::Async::Webservice::UPS;

 my $ups = Net::Async::Webservice::UPS->new({
   config_file => $ENV{HOME}.'/.naws_ups.conf',
   user_agent => LWP::UserAgent->new,
 });

 my $response = $ups->validate_address($postcode)->get;

 say $_->postal_code for @{$response->addresses};

DESCRIPTION

This class implements some of the methods of the UPS API, using Net::Async::HTTP as a user agent by default (you can still pass something like LWP::UserAgent and it will work). All methods that perform API calls return Futures (if using a synchronous user agent, all the Futures will be returned already completed).

NOTE: I've kept many names and codes from the original Net::UPS, so the API of this distribution may look a bit strange. It should make it simpler to migrate from Net::UPS, though.

ATTRIBUTES

live_mode

Boolean, defaults to false. When set to true, the live API endpoint will be used, otherwise the test one will. Flipping this attribute will reset "base_url", so you generally don't want to touch this if you're using some custom API endpoint.

base_url

A URI object, coercible from a string. The base URL to use to send API requests to (actual requests will be POSTed to an actual URL built from this by appending the appropriate service path). Defaults to the standard UPS endpoints:

  • https://onlinetools.ups.com/ups.app/xml for live

  • https://wwwcie.ups.com/ups.app/xml for testing

See also "live_mode".

user_id

password

access_key

Strings, required. Authentication credentials.

account_number

String. Used in some requests as "shipper number".

customer_classification

String, usually one of WHOLESALE, OCCASIONAL, RETAIL. Used when requesting rates.

pickup_type

String, defaults to ONE_TIME. Used when requesting rates.

cache

Responses are cached if this is set. You can pass your own cache object (that implements the get and set methods like CHI does), or use the cache_life and cache_root constructor parameters to get a CHI instance based on CHI::Driver::File.

user_agent

A user agent object, looking either like Net::Async::HTTP (has do_request and POST) or like LWP::UserAgent (has request and post). You can pass the loop constructor parameter to get a default Net::Async::HTTP instance.

ssl_options

Optional hashref, its contents will be passed to user_agent's do_request method.

If IO::Socket::SSL and Mozilla::CA are installed, the default value sets full TLS validation, and makes sure that the Verisign certificate currently (as of 2015-02-03) used by the UPS servers is recognised (see "TLS notes" in UPS SSL).

METHODS

does_caching

Returns a true value if caching is enabled.

new

Async:

  my $ups = Net::Async::Webservice::UPS->new({
     loop => $loop,
     config_file => $file_name,
     cache_life => 5,
  });

Sync:

  my $ups = Net::Async::Webservice::UPS->new({
     user_agent => LWP::UserAgent->new,
     config_file => $file_name,
     cache_life => 5,
  });

In addition to passing all the various attributes values, you can use a few shortcuts.

loop

a IO::Async::Loop; a locally-constructed Net::Async::HTTP will be registered to it and set as "user_agent"

config_file

a path name; will be parsed with Config::Any, and the values used as if they had been passed in to the constructor

cache_life

lifetime, in minutes, of cache entries; a "cache" will be built automatically if this is set (using CHI with the File driver)

cache_root

where to store the cache files for the default cache object, defaults to naws_ups under your system's temporary directory

A few more examples:

  • no config file, no cache, async:

       ->new({
         user_id=>$user,password=>$pw,access_key=>$ak,
         loop=>$loop,
       }),
  • no config file, no cache, custom user agent (sync or async):

       ->new({
         user_id=>$user,password=>$pw,access_key=>$ak,
         user_agent=>$ua,
       }),

    it's your job to register the custom user agent to the event loop, if you're using an async agent

  • config file, async, custom cache:

       ->new({
         loop=>$loop,
         cache=>CHI->new(...),
       }),

transaction_reference

Constant data used to fill something in requests. I don't know what it's for, I just copied it from Net::UPS.

access_as_xml

Returns a XML document with the credentials.

request_rate

  $ups->request_rate({
    from => $address_a,
    to => $address_b,
    packages => [ $package_1, $package_2 ],
  }) ==> (Net::Async::Webservice::UPS::Response::Rate)

from and to are instances of Net::Async::Webservice::UPS::Address, or postcode strings that will be coerced to addresses.

packages is an arrayref of Net::Async::Webservice::UPS::Package (or a single package, will be coerced to a 1-element array ref).

NOTE: the id field of the packages used to be modified. It no longer is.

Optional parameters:

limit_to

only accept some services (see "ServiceLabel" in Net::Async::Webservice::UPS::Types)

exclude

exclude some services (see "ServiceLabel" in Net::Async::Webservice::UPS::Types)

mode

defaults to rate, could be shop

service

defaults to GROUND, see Net::Async::Webservice::UPS::Service

customer_context

optional string for reference purposes

The Future returned will yield an instance of Net::Async::Webservice::UPS::Response::Rate, or fail with an exception.

Identical requests can be cached.

validate_address

  $ups->validate_address($address)
    ==> (Net::Async::Webservice::UPS::Response::Address)

  $ups->validate_address($address,$tolerance)
    ==> (Net::Async::Webservice::UPS::Response::Address)

$address is an instance of Net::Async::Webservice::UPS::Address, or a postcode string that will be coerced to an address.

Optional parameter: a tolerance (float, between 0 and 1). Returned addresses with quality below 1 minus tolerance will be filtered out.

The Future returned will yield an instance of Net::Async::Webservice::UPS::Response::Address, or fail with an exception.

Identical requests can be cached.

validate_street_address

  $ups->validate_street_address($address)
    ==> (Net::Async::Webservice::UPS::Response::Address)

$address is an instance of Net::Async::Webservice::UPS::Address, or a postcode string that will be coerced to an address.

The Future returned will yield an instance of Net::Async::Webservice::UPS::Response::Address, or fail with an exception.

Identical requests can be cached.

ship_confirm

  $ups->ship_confirm({
     from => $source_contact,
     to => $destination_contact,
     description => 'something',
     payment => $payment_method,
     packages => \@packages,
  }) ==> $shipconfirm_response

Performs a ShipConfirm request to UPS. The parameters are:

from

required, instance of Net::Async::Webservice::UPS::Contact, where the shipments starts from

to

required, instance of Net::Async::Webservice::UPS::Contact, where the shipments has to be delivered to

shipper

optional, instance of Net::Async::Webservice::UPS::Shipper, who is requesting the shipment; if not specified, it's taken to be the same as the from with the "account_number" of this UPS object

service

the shipping service to use, see "Service" in Net::Async::Webservice::UPS::Types, defaults to GROUND

description

required string, description of the shipment

payment

required instance of Net::Async::Webservice::UPS::Payment, how to pay for this shipment

label

optional instance of Net::Async::Webservice::UPS::Label, what kind of label to request

packages

an arrayref of Net::Async::Webservice::UPS::Package (or a single package, will be coerced to a 1-element array ref), the packages to ship

return_service

optional, instance of Net::Async::Webservice::UPS::ReturnService, what kind of return service to request

delivery_confirmation

optional, 1 means "signature required", 2 mean "adult signature required"

customer_context

optional string for reference purposes

Returns a Future yielding an instance of Net::Async::Webservice::UPS::Response::ShipmentConfirm.

NOTE: the API of this call may change in the future, let me know if features you need are missing or badly understood!

ship_accept

  $ups->ship_accept({
      confirm => $shipconfirm_response,
  }) ==> $shipaccept_response

Performs a ShipAccept request to UPS. The parameters are:

confirm

required, instance of Net::Async::Webservice::UPS::Response::ShipmentConfirm,as returned by "ship_confirm"

customer_context

optional string for reference purposes

Returns a Future yielding an instance of Net::Async::Webservice::UPS::Response::ShipmentAccept.

qv_events

  $ups->qv_events({
      subscriptions => [ Net::Async::Webservice::UPS::QVSubscription->new(
        name => 'MySubscription',
      ) ],
  }) ==> $qv_response

Performs a QVEvennts request to UPS. The parameters are:

subscriptions

optional, array of Net::Async::Webservice::UPS::QVSubscription, specifying what you want to retrieve

bookmark

optional, string retrieved from a previous call, used for pagination (see Net::Async::Webservice::UPS::Response::QV)

customer_context

optional string for reference purposes

Returns a Future yielding an instance of Net::Async::Webservice::UPS::Response::QV.

xml_request

  $ups->xml_request({
    url_suffix => $string,
    data => \%request_data,
    XMLout => \%xml_simple_out_options,
    XMLin => \%xml_simple_in_options,
  }) ==> ($parsed_response);

This method is mostly internal, you shouldn't need to call it.

It builds a request XML document by concatenating the output of "access_as_xml" with whatever XML::Simple produces from the given data and XMLout options.

It then posts (possibly asynchronously) this to the URL obtained concatenating "base_url" with url_suffix (see the "post" method). If the request is successful, it parses the body (with XML::Simple using the XMLin options) and completes the returned future with the result.

If the parsed response contains a non-zero /Response/ResponseStatusCode, the returned future will fail with a Net::Async::Webservice::UPS::Exception::UPSError instance.

post

  $ups->post($url_suffix,$body) ==> ($decoded_content)

Posts the given $body to the URL obtained concatenating "base_url" with $url_suffix. If the request is successful, it completes the returned future with the decoded content of the response, otherwise it fails the future with a Net::Async::Webservice::Common::Exception::HTTPError instance.

generate_cache_key

Generates a cache key (a string) identifying a request. Two requests with the same cache key should return the same response.

UPS SSL/TLS notes

In December 2014, UPS notified all its users that it would stop supporting SSLv3 in March 2015. This library has no problems with that, since LWP has supported TLS for years.

Another, unrelated, issue cropped up at rougly the same time, to confuse the situation: Mozilla::CA, which is used to get the root certificates to verify connections, dropped a top-level Verisign certificate that Verisign stopped using in 2010, but the UPS servers' certificate was signed with it, so LWP stopped recognising the servers' certificate. Net::Async::Webservice::UPS 1.1.3 works around the problem by always including the root certificate in the default "ssl_options". If you use custom options, you may want to check that you're including the correct certificate. See also https://rt.cpan.org/Ticket/Display.html?id=101908

AUTHORS

  • Gianni Ceccarelli <gianni.ceccarelli@net-a-porter.com>

  • Sherzod B. Ruzmetov <sherzodr@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Gianni Ceccarelli <gianni.ceccarelli@net-a-porter.com>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.