Adding initial shipping API version.
parent
a7158e2781
commit
5590fc75a4
@ -1,2 +1,226 @@
|
||||
shipping
|
||||
========
|
||||
PHP Shipping API
|
||||
================
|
||||
|
||||
A shipping rate wrapper for USPS, UPS, and Fedex.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the following lines to your ``composer.json`` file.
|
||||
|
||||
```JSON
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/pdt256/shipping.git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"pdt256/shipping": "dev-master"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Create a shipment object:
|
||||
|
||||
```php
|
||||
$shipment = [
|
||||
'weight' => 3, // lbs
|
||||
'dimensions' => [
|
||||
'width' => 9, // inches
|
||||
'length' => 9,
|
||||
'height' => 9,
|
||||
],
|
||||
'from' => [
|
||||
'postal_code' => '90401',
|
||||
'country_code' => 'US',
|
||||
],
|
||||
'to' => [
|
||||
'postal_code' => '78703',
|
||||
'country_code' => 'US',
|
||||
'is_residential' => TRUE,
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
## UPS (Stub) Example
|
||||
|
||||
Below is an example request to get shipping rates from the UPS API.
|
||||
|
||||
Notice: The below line uses a stub class to fake a response from the UPS API.
|
||||
You can immediately use this method in your code until you get an account with UPS.
|
||||
```
|
||||
'request_adapter' => new RateRequest\StubUPS(),
|
||||
```
|
||||
|
||||
```php
|
||||
use pdt256\Shipping\UPS;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
|
||||
$ups = new UPS\Rate([
|
||||
'prod' => FALSE,
|
||||
'access_key' => 'XXXX',
|
||||
'user_id' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'shipper_number' => 'XXXX',
|
||||
'shipment' => $shipment,
|
||||
'approved_codes' => [
|
||||
'03', // 1-5 business days
|
||||
'02', // 2 business days
|
||||
'01', // next business day 10:30am
|
||||
'13', // next business day by 3pm
|
||||
'14', // next business day by 8am
|
||||
],
|
||||
'request_adapter' => new RateRequest\StubUPS(),
|
||||
]);
|
||||
|
||||
$ups_rates = $ups->get_rates();
|
||||
```
|
||||
|
||||
Output array sorted by cost: (in cents)
|
||||
|
||||
```php
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'code' => '03',
|
||||
'name' => 'UPS Ground',
|
||||
'cost' => 1900,
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'code' => '02',
|
||||
'name' => 'UPS 2nd Day Air',
|
||||
'cost' => 4900,
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'code' => '13',
|
||||
'name' => 'UPS Next Day Air Saver',
|
||||
'cost' => 8900,
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'code' => '01',
|
||||
'name' => 'UPS Next Day Air',
|
||||
'cost' => 9300,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## USPS (Stub) Example
|
||||
|
||||
```php
|
||||
use pdt256\Shipping\USPS;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
|
||||
$usps = new USPS\Rate([
|
||||
'prod' => FALSE,
|
||||
'username' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'shipment' => array_merge($shipment, [
|
||||
'size' => 'LARGE',
|
||||
'container' => 'RECTANGULAR',
|
||||
]),
|
||||
'approved_codes' => [
|
||||
'1', // 1-3 business days
|
||||
'4', // 2-8 business days
|
||||
],
|
||||
'request_adapter' => new RateRequest\StubUSPS(),
|
||||
]);
|
||||
|
||||
$usps_rates = $usps->get_rates();
|
||||
```
|
||||
|
||||
Output array sorted by cost: (in cents)
|
||||
|
||||
```php
|
||||
array (
|
||||
1 =>
|
||||
array (
|
||||
'code' => '4',
|
||||
'name' => 'Parcel Post',
|
||||
'cost' => 1000,
|
||||
),
|
||||
0 =>
|
||||
array (
|
||||
'code' => '1',
|
||||
'name' => 'Priority Mail',
|
||||
'cost' => 1200,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Fedex (Stub) Example
|
||||
|
||||
```php
|
||||
use pdt256\Shipping\Fedex;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
|
||||
$fedex = new Fedex\Rate([
|
||||
'prod' => FALSE,
|
||||
'key' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'account_number' => 'XXXX',
|
||||
'meter_number' => 'XXXX',
|
||||
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
|
||||
'shipment' => array_merge($shipment, [
|
||||
'packaging_type' => 'YOUR_PACKAGING',
|
||||
]),
|
||||
'approved_codes' => [
|
||||
'FEDEX_EXPRESS_SAVER', // 1-3 business days
|
||||
'FEDEX_GROUND', // 1-5 business days
|
||||
'GROUND_HOME_DELIVERY', // 1-5 business days
|
||||
'FEDEX_2_DAY', // 2 business days
|
||||
'STANDARD_OVERNIGHT', // overnight
|
||||
],
|
||||
'request_adapter' => new RateRequest\StubFedex(),
|
||||
]);
|
||||
|
||||
$fedex_rates = $fedex->get_rates();
|
||||
```
|
||||
|
||||
Output array sorted by cost: (in cents)
|
||||
|
||||
```php
|
||||
array (
|
||||
3 =>
|
||||
array (
|
||||
'code' => 'GROUND_HOME_DELIVERY',
|
||||
'name' => 'Ground Home Delivery',
|
||||
'cost' => 1600,
|
||||
'delivery_ts' => NULL,
|
||||
'transit_time' => 'THREE_DAYS',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'code' => 'FEDEX_EXPRESS_SAVER',
|
||||
'name' => 'Fedex Express Saver',
|
||||
'cost' => 2900,
|
||||
'delivery_ts' => '2014-09-30T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'code' => 'FEDEX_2_DAY',
|
||||
'name' => 'Fedex 2 Day',
|
||||
'cost' => 4000,
|
||||
'delivery_ts' => '2014-09-29T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
),
|
||||
0 =>
|
||||
array (
|
||||
'code' => 'STANDARD_OVERNIGHT',
|
||||
'name' => 'Standard Overnight',
|
||||
'cost' => 7800,
|
||||
'delivery_ts' => '2014-09-26T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### License
|
||||
|
||||
[MIT license](http://opensource.org/licenses/MIT)
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false">
|
||||
<testsuites>
|
||||
<testsuite name="Ship Test Suite">
|
||||
<directory suffix=".php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping;
|
||||
|
||||
class Arr
|
||||
{
|
||||
public static function get($array, $key, $default = NULL)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : $default;
|
||||
}
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\Fedex;
|
||||
|
||||
use pdt256\Shipping;
|
||||
use pdt256\Shipping\Arr;
|
||||
use pdt256\Shipping\RateAdapter;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
use DOMDocument;
|
||||
use Exception;
|
||||
|
||||
class Rate extends RateAdapter
|
||||
{
|
||||
private $url_dev = 'https://gatewaybeta.fedex.com/web-services/';
|
||||
private $url_prod = 'https://gateway.fedex.com/web-services/';
|
||||
|
||||
private $key = 'XXX';
|
||||
private $password = 'XXX';
|
||||
private $account_number = 'XXX';
|
||||
private $meter_number = 'XXX';
|
||||
private $drop_off_type = 'BUSINESS_SERVICE_CENTER';
|
||||
|
||||
public $approved_codes = [
|
||||
'PRIORITY_OVERNIGHT',
|
||||
'FEDEX_2_DAY',
|
||||
'FEDEX_EXPRESS_SAVER',
|
||||
'FEDEX_GROUND',
|
||||
'GROUND_HOME_DELIVERY',
|
||||
];
|
||||
|
||||
private $shipping_codes = [
|
||||
'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => 'Europe First International Priority',
|
||||
'FEDEX_1_DAY_FREIGHT' => 'Fedex 1 Day Freight',
|
||||
'FEDEX_2_DAY' => 'Fedex 2 Day',
|
||||
'FEDEX_2_DAY_AM' => 'Fedex 2 Day AM',
|
||||
'FEDEX_2_DAY_FREIGHT' => 'Fedex 2 Day Freight',
|
||||
'FEDEX_3_DAY_FREIGHT' => 'Fedex 3 Day Freight',
|
||||
'FEDEX_EXPRESS_SAVER' => 'Fedex Express Saver',
|
||||
'FEDEX_FIRST_FREIGHT' => 'Fedex First Freight',
|
||||
'FEDEX_FREIGHT_ECONOMY' => 'Fedex Freight Economy',
|
||||
'FEDEX_FREIGHT_PRIORITY' => 'Fedex Freight Priority',
|
||||
'FEDEX_GROUND' => 'Fedex Ground',
|
||||
'FIRST_OVERNIGHT' => 'First Overnight',
|
||||
'GROUND_HOME_DELIVERY' => 'Ground Home Delivery',
|
||||
'INTERNATIONAL_ECONOMY' => 'International Economy',
|
||||
'INTERNATIONAL_ECONOMY_FREIGHT' => 'International Economy Freight',
|
||||
'INTERNATIONAL_FIRST' => 'International First',
|
||||
'INTERNATIONAL_PRIORITY' => 'International Priority',
|
||||
'INTERNATIONAL_PRIORITY_FREIGHT' => 'International Priority Freight',
|
||||
'PRIORITY_OVERNIGHT' => 'Priority Overnight',
|
||||
'SMART_POST' => 'Smart Post',
|
||||
'STANDARD_OVERNIGHT' => 'Standard Overnight',
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
if (isset($options['key'])) {
|
||||
$this->key = $options['key'];
|
||||
}
|
||||
|
||||
if (isset($options['password'])) {
|
||||
$this->password = $options['password'];
|
||||
}
|
||||
|
||||
if (isset($options['account_number'])) {
|
||||
$this->account_number = $options['account_number'];
|
||||
}
|
||||
|
||||
if (isset($options['meter_number'])) {
|
||||
$this->meter_number = $options['meter_number'];
|
||||
}
|
||||
|
||||
if (isset($options['approved_codes'])) {
|
||||
$this->approved_codes = $options['approved_codes'];
|
||||
}
|
||||
|
||||
if (isset($options['drop_off_type'])) {
|
||||
$this->drop_off_type = $options['drop_off_type'];
|
||||
}
|
||||
|
||||
if (isset($options['request_adapter'])) {
|
||||
$this->set_request_adapter($options['request_adapter']);
|
||||
} else {
|
||||
$this->set_request_adapter(new RateRequest\Post());
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
$to = Arr::get($this->shipment, 'to');
|
||||
$shipper = Arr::get($this->shipment, 'from');
|
||||
$dimensions = Arr::get($this->shipment, 'dimensions');
|
||||
|
||||
$pounds = (int) Arr::get($this->shipment, 'weight');
|
||||
$ounces = 0;
|
||||
|
||||
if ($pounds < 1) {
|
||||
throw new Exception('Weight missing');
|
||||
}
|
||||
|
||||
$date = time();
|
||||
$day_name = date('l', $date);
|
||||
|
||||
if ($day_name == 'Saturday') {
|
||||
$date += 172800;
|
||||
} elseif ($day_name == 'Sunday') {
|
||||
$date += 86400;
|
||||
}
|
||||
|
||||
// http://www.fedex.com/templates/components/apps/wpor/secure/downloads/pdf/Aug13/PropDevGuide.pdf
|
||||
// http://www.fedex.com/us/developer/product/WebServices/MyWebHelp_August2010/Content/Proprietary_Developer_Guide/Rate_Services_conditionalized.htm
|
||||
|
||||
$this->data =
|
||||
'<?xml version="1.0"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://fedex.com/ws/rate/v13">
|
||||
<SOAP-ENV:Body>
|
||||
<RateRequest>
|
||||
<WebAuthenticationDetail>
|
||||
<UserCredential>
|
||||
<Key>' . $this->key . '</Key>
|
||||
<Password>' . $this->password . '</Password>
|
||||
</UserCredential>
|
||||
</WebAuthenticationDetail>
|
||||
<ClientDetail>
|
||||
<AccountNumber>' . $this->account_number . '</AccountNumber>
|
||||
<MeterNumber>' . $this->meter_number . '</MeterNumber>
|
||||
</ClientDetail>
|
||||
<Version>
|
||||
<ServiceId>crs</ServiceId>
|
||||
<Major>13</Major>
|
||||
<Intermediate>0</Intermediate>
|
||||
<Minor>0</Minor>
|
||||
</Version>
|
||||
<ReturnTransitAndCommit>true</ReturnTransitAndCommit>
|
||||
<RequestedShipment>
|
||||
<ShipTimestamp>' . date('c') . '</ShipTimestamp>
|
||||
<DropoffType>' . $this->drop_off_type . '</DropoffType>
|
||||
<PackagingType>' . Arr::get($this->shipment, 'packaging_type') . '</PackagingType>
|
||||
<Shipper>
|
||||
<Address>
|
||||
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
|
||||
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
|
||||
' . ((Arr::get($shipper, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
|
||||
</Address>
|
||||
</Shipper>
|
||||
<Recipient>
|
||||
<Address>
|
||||
<PostalCode>' . Arr::get($to, 'postal_code') . '</PostalCode>
|
||||
<CountryCode>' . Arr::get($to, 'country_code') . '</CountryCode>
|
||||
' . ((Arr::get($to, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
|
||||
</Address>
|
||||
</Recipient>
|
||||
<RateRequestTypes>LIST</RateRequestTypes>
|
||||
<PackageCount>1</PackageCount>
|
||||
<RequestedPackageLineItems>
|
||||
<SequenceNumber>1</SequenceNumber>
|
||||
<GroupPackageCount>1</GroupPackageCount>
|
||||
<Weight>
|
||||
<Units>LB</Units>
|
||||
<Value>' . $pounds . '</Value>
|
||||
</Weight>
|
||||
<Dimensions>
|
||||
<Length>' . Arr::get($dimensions, 'length') . '</Length>
|
||||
<Width>' . Arr::get($dimensions, 'width') . '</Width>
|
||||
<Height>' . Arr::get($dimensions, 'height') . '</Height>
|
||||
<Units>IN</Units>
|
||||
</Dimensions>
|
||||
</RequestedPackageLineItems>
|
||||
</RequestedShipment>
|
||||
</RateRequest>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function execute()
|
||||
{
|
||||
if ($this->is_prod) {
|
||||
$url = $this->url_prod;
|
||||
} else {
|
||||
$url = $this->url_dev;
|
||||
}
|
||||
|
||||
$this->response = $this->rate_request->execute($url, $this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function process()
|
||||
{
|
||||
try {
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$dom->loadXml($this->response);
|
||||
$rate_reply = $dom->getElementsByTagName('RateReplyDetails');
|
||||
|
||||
if (empty($rate_reply->length)) {
|
||||
throw new Exception('Unable to get FedEx Rates.');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// StatsD::increment('error.shipping.get_fedex_rate');
|
||||
// Kohana::$log->add(Log::ERROR, $e)->write();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
foreach ($rate_reply as $rate) {
|
||||
$code = $rate->getElementsByTagName('ServiceType')->item(0)->nodeValue;
|
||||
|
||||
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = Arr::get($this->shipping_codes, $code);
|
||||
|
||||
$delivery_ts = @$rate->getElementsByTagName('DeliveryTimestamp')->item(0)->nodeValue;
|
||||
$transit_time = @$rate->getElementsByTagName('TransitTime')->item(0)->nodeValue;
|
||||
|
||||
$cost = $rate
|
||||
->getElementsByTagName('RatedShipmentDetails')->item(0)
|
||||
->getElementsByTagName('ShipmentRateDetail')->item(0)
|
||||
->getElementsByTagName('TotalNetCharge')->item(0)
|
||||
->getElementsByTagName('Amount')->item(0)->nodeValue;
|
||||
|
||||
$this->rates[] = array(
|
||||
'code' => $code,
|
||||
'name' => $name,
|
||||
'cost' => (int) $cost * 100,
|
||||
'delivery_ts' => $delivery_ts,
|
||||
'transit_time' => $transit_time,
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping;
|
||||
|
||||
use Exception;
|
||||
|
||||
abstract class RateAdapter
|
||||
{
|
||||
protected $is_prod = FALSE;
|
||||
|
||||
protected $shipment;
|
||||
protected $data;
|
||||
protected $response;
|
||||
protected $rates = [];
|
||||
|
||||
protected $rate_request;
|
||||
|
||||
abstract protected function prepare(); // Prepare XML
|
||||
abstract protected function execute(); // Curl Request
|
||||
abstract protected function process(); // Convert to shipping rates array
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (isset($options['prod'])) {
|
||||
$this->is_prod = (bool) $options['prod'];
|
||||
}
|
||||
|
||||
if (isset($options['shipment'])) {
|
||||
$this->shipment = $options['shipment'];
|
||||
}
|
||||
|
||||
if (empty($this->shipment['to'])) {
|
||||
throw new Exception('Shipment "to" missing');
|
||||
}
|
||||
|
||||
if (empty($this->shipment['from'])) {
|
||||
throw new Exception('Shipment "from" missing');
|
||||
}
|
||||
|
||||
if (empty($this->shipment['dimensions'])) {
|
||||
throw new Exception('Shipment "dimensions" missing');
|
||||
}
|
||||
}
|
||||
|
||||
public function set_request_adapter(RateRequest\Adapter $rate_request)
|
||||
{
|
||||
$this->rate_request = $rate_request;
|
||||
}
|
||||
|
||||
public function get_rates()
|
||||
{
|
||||
$this
|
||||
->prepare()
|
||||
->execute()
|
||||
->process()
|
||||
->sort_by_cost();
|
||||
|
||||
return $this->rates;
|
||||
}
|
||||
|
||||
protected function sort_by_cost()
|
||||
{
|
||||
uasort($this->rates, create_function('$a, $b', 'return ($a["cost"] > $b["cost"]);'));
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\RateRequest;
|
||||
|
||||
abstract class Adapter
|
||||
{
|
||||
protected $curl_connect_timeout_ms = 1000; // milliseconds
|
||||
protected $curl_dl_timeout = 11; // seconds
|
||||
|
||||
abstract public function execute($url, $data = NULL);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\RateRequest;
|
||||
|
||||
class Get extends Adapter
|
||||
{
|
||||
public function execute($url, $data = NULL)
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($ch, CURLOPT_HTTPGET, TRUE);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->curl_connect_timeout_ms);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_dl_timeout);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\RateRequest;
|
||||
|
||||
class Post extends Adapter
|
||||
{
|
||||
public function execute($url, $data = NULL)
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($ch, CURLOPT_POST, TRUE);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->curl_connect_timeout_ms);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_dl_timeout);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\RateRequest;
|
||||
|
||||
class StubUSPS extends Adapter
|
||||
{
|
||||
private $artificial_delay = 0;
|
||||
|
||||
public function __construct($artificial_delay = 0)
|
||||
{
|
||||
$this->artificial_delay = $artificial_delay;
|
||||
}
|
||||
|
||||
public function execute($url, $data = NULL)
|
||||
{
|
||||
if ($this->artificial_delay > 0) {
|
||||
sleep($this->artificial_delay);
|
||||
}
|
||||
|
||||
$response = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<RateV4Response><Package ID="1"><ZipOrigination>90401</ZipOrigination><ZipDestination>76667</ZipDestination><Pounds>3</Pounds><Ounces>0</Ounces><Size>LARGE</Size><Machinable>FALSE</Machinable><Zone>6</Zone><Postage CLASSID="3"><MailService>Priority Mail Express 2-Day&lt;sup&gt;&#8482;&lt;/sup&gt;</MailService><Rate>42.25</Rate></Postage><Postage CLASSID="2"><MailService>Priority Mail Express 2-Day&lt;sup&gt;&#8482;&lt;/sup&gt; Hold For Pickup</MailService><Rate>42.25</Rate></Postage><Postage CLASSID="1"><MailService>Priority Mail 2-Day&lt;sup&gt;&#8482;&lt;/sup&gt;</MailService><Rate>12.20</Rate></Postage><Postage CLASSID="4"><MailService>Standard Post&lt;sup&gt;&#174;&lt;/sup&gt;</MailService><Rate>10.01</Rate></Postage><Postage CLASSID="6"><MailService>Media Mail Parcel</MailService><Rate>3.65</Rate></Postage><Postage CLASSID="7"><MailService>Library Mail Parcel</MailService><Rate>3.48</Rate></Postage></Package></RateV4Response>';
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping;
|
||||
|
||||
class Ship
|
||||
{
|
||||
protected $shipping_options = [
|
||||
'Standard Shipping' => [
|
||||
'ups' => [
|
||||
'03' => '1-5 business days',
|
||||
],
|
||||
'fedex' => [
|
||||
'FEDEX_EXPRESS_SAVER' => '1-3 business days',
|
||||
'FEDEX_GROUND' => '1-5 business days',
|
||||
'GROUND_HOME_DELIVERY' => '1-5 business days',
|
||||
],
|
||||
'usps' => [
|
||||
'1' => '1-3 business days',
|
||||
'4' => '2-8 business days',
|
||||
],
|
||||
],
|
||||
'Two-Day Shipping' => [
|
||||
'ups' => [
|
||||
'02' => '2 business days',
|
||||
],
|
||||
'fedex' => [
|
||||
'FEDEX_2_DAY' => '2 business days',
|
||||
],
|
||||
],
|
||||
'One-Day Shipping' => [
|
||||
'ups' => [
|
||||
'01' => 'next business day 10:30am',
|
||||
'13' => 'next business day by 3pm',
|
||||
'14' => 'next business day by 8am',
|
||||
],
|
||||
'fedex' => [
|
||||
'STANDARD_OVERNIGHT' => 'overnight',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
public static function factory($shipping_options = [])
|
||||
{
|
||||
$object = new self();
|
||||
|
||||
if ( ! empty($shipping_options)) {
|
||||
$object->shipping_options = $shipping_options;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function get_approved_codes($carrier = NULL) {
|
||||
$approved_codes = [];
|
||||
|
||||
// Build approved_codes
|
||||
foreach ($this->shipping_options as $shipping_group => $row) {
|
||||
|
||||
foreach ($row as $_carrier => $row2) {
|
||||
if ( ! isset($approved_codes[$_carrier])) {
|
||||
$approved_codes[$_carrier] = [];
|
||||
}
|
||||
|
||||
foreach ($row2 as $code => $display) {
|
||||
$approved_codes[$_carrier][] = $code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($carrier !== NULL AND isset($approved_codes[$carrier])) {
|
||||
return $approved_codes[$carrier];
|
||||
}
|
||||
|
||||
return $approved_codes;
|
||||
}
|
||||
|
||||
public function get_display_rates($rates)
|
||||
{
|
||||
// Build output array with cheapest shipping option for each group
|
||||
$display_rates = [];
|
||||
foreach ($this->shipping_options as $shipping_group => $row) {
|
||||
$display_rates[$shipping_group] = [];
|
||||
$cheapest_row = NULL;
|
||||
|
||||
foreach ($row as $carrier => $row2) {
|
||||
$group_codes = array_keys($row2);
|
||||
|
||||
if ( ! empty($rates[$carrier])) {
|
||||
|
||||
foreach ($rates[$carrier] as $row3) {
|
||||
|
||||
if (in_array($row3['code'], $group_codes)) {
|
||||
$row3['carrier'] = $carrier;
|
||||
|
||||
if ($cheapest_row === NULL) {
|
||||
$cheapest_row = $row3;
|
||||
} else {
|
||||
if ($row3['cost'] < $cheapest_row['cost']) {
|
||||
$cheapest_row = $row3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$display_rates[$shipping_group][] = $cheapest_row;
|
||||
}
|
||||
|
||||
return $display_rates;
|
||||
}
|
||||
|
||||
public function get_all_display_rates($rates)
|
||||
{
|
||||
// Build output array listing all group options
|
||||
$display_rates = [];
|
||||
foreach ($this->shipping_options as $shipping_group => $row) {
|
||||
$display_rates[$shipping_group] = [];
|
||||
|
||||
foreach ($row as $carrier => $row2) {
|
||||
$group_codes = array_keys($row2);
|
||||
|
||||
if ( ! empty($rates[$carrier])) {
|
||||
|
||||
foreach ($rates[$carrier] as $row3) {
|
||||
|
||||
if (in_array($row3['code'], $group_codes)) {
|
||||
$row3['carrier'] = $carrier;
|
||||
$display_rates[$shipping_group][] = $row3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->sort_by_cost($display_rates[$shipping_group]);
|
||||
}
|
||||
|
||||
return $display_rates;
|
||||
}
|
||||
|
||||
protected function sort_by_cost( & $rates)
|
||||
{
|
||||
uasort($rates, create_function('$a, $b', 'return ($a["cost"] > $b["cost"]);'));
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\UPS;
|
||||
|
||||
use pdt256\Ship;
|
||||
use pdt256\Shipping\Arr;
|
||||
use pdt256\Shipping\RateAdapter;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
use DOMDocument;
|
||||
use Exception;
|
||||
|
||||
class Rate extends RateAdapter
|
||||
{
|
||||
private $url_dev = 'https://wwwcie.ups.com/ups.app/xml/Rate';
|
||||
private $url_prod = 'https://www.ups.com/ups.app/xml/Rate';
|
||||
|
||||
private $access_key = 'XXX';
|
||||
private $user_id = 'XXX';
|
||||
private $password = 'XXX';
|
||||
private $shipper_number = 'XXX';
|
||||
|
||||
public $approved_codes = [
|
||||
'03',
|
||||
'12',
|
||||
];
|
||||
|
||||
private $shipping_codes = [
|
||||
'US' => [ // United States
|
||||
'01' => 'UPS Next Day Air',
|
||||
'02' => 'UPS 2nd Day Air',
|
||||
'03' => 'UPS Ground',
|
||||
'07' => 'UPS Worldwide Express',
|
||||
'08' => 'UPS Worldwide Expedited',
|
||||
'11' => 'UPS Standard',
|
||||
'12' => 'UPS 3 Day Select',
|
||||
'13' => 'UPS Next Day Air Saver',
|
||||
'14' => 'UPS Next Day Air Early A.M.',
|
||||
'54' => 'UPS Worldwide Express Plus',
|
||||
'59' => 'UPS 2nd Day Air A.M.',
|
||||
'65' => 'UPS Saver',
|
||||
],
|
||||
'CA' => [ // Canada
|
||||
'01' => 'UPS Express',
|
||||
'02' => 'UPS Expedited',
|
||||
'07' => 'UPS Worldwide Express',
|
||||
'08' => 'UPS Worldwide Expedited',
|
||||
'11' => 'UPS Standard',
|
||||
'12' => 'UPS 3 Day Select',
|
||||
'13' => 'UPS Saver',
|
||||
'14' => 'UPS Express Early A.M.',
|
||||
'54' => 'UPS Worldwide Express Plus',
|
||||
'65' => 'UPS Saver',
|
||||
],
|
||||
'EU' => [ // European Union
|
||||
'07' => 'UPS Express',
|
||||
'08' => 'UPS Expedited',
|
||||
'11' => 'UPS Standard',
|
||||
'54' => 'UPS Worldwide Express Plus',
|
||||
'65' => 'UPS Saver',
|
||||
'82' => 'UPS Today Standard',
|
||||
'83' => 'UPS Today Dedicated Courier',
|
||||
'84' => 'UPS Today Intercity',
|
||||
'85' => 'UPS Today Express',
|
||||
'86' => 'UPS Today Express Saver',
|
||||
'01' => 'UPS Next Day Air',
|
||||
'02' => 'UPS 2nd Day Air',
|
||||
'03' => 'UPS Ground',
|
||||
'14' => 'UPS Next Day Air Early A.M.',
|
||||
],
|
||||
'MX' => [ // Mexico
|
||||
'07' => 'UPS Express',
|
||||
'08' => 'UPS Expedited',
|
||||
'54' => 'UPS Express Plus',
|
||||
'65' => 'UPS Saver',
|
||||
],
|
||||
'other' => [ // Other
|
||||
'07' => 'UPS Express',
|
||||
'08' => 'UPS Worldwide Expedited',
|
||||
'11' => 'UPS Standard',
|
||||
'54' => 'UPS Worldwide Express Plus',
|
||||
'65' => 'UPS Saver',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
if (isset($options['access_key'])) {
|
||||
$this->access_key = $options['access_key'];
|
||||
}
|
||||
|
||||
if (isset($options['user_id'])) {
|
||||
$this->user_id = $options['user_id'];
|
||||
}
|
||||
|
||||
if (isset($options['password'])) {
|
||||
$this->password = $options['password'];
|
||||
}
|
||||
|
||||
if (isset($options['shipper_number'])) {
|
||||
$this->shipper_number = $options['shipper_number'];
|
||||
}
|
||||
|
||||
if (isset($options['approved_codes'])) {
|
||||
$this->approved_codes = $options['approved_codes'];
|
||||
}
|
||||
|
||||
if (isset($options['request_adapter'])) {
|
||||
$this->set_request_adapter($options['request_adapter']);
|
||||
} else {
|
||||
$this->set_request_adapter(new RateRequest\Post());
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
$to = Arr::get($this->shipment, 'to');
|
||||
$shipper = Arr::get($this->shipment, 'from');
|
||||
$dimensions = Arr::get($this->shipment, 'dimensions');
|
||||
|
||||
$pounds = (int) Arr::get($this->shipment, 'weight');
|
||||
$ounces = 0;
|
||||
|
||||
if ($pounds < 1) {
|
||||
throw new Exception('Weight missing');
|
||||
}
|
||||
|
||||
$service_code = '03';
|
||||
|
||||
$this->data =
|
||||
'<?xml version="1.0"?>
|
||||
<AccessRequest xml:lang="en-US">
|
||||
<AccessLicenseNumber>' . $this->access_key . '</AccessLicenseNumber>
|
||||
<UserId>' . $this->user_id . '</UserId>
|
||||
<Password>' . $this->password . '</Password>
|
||||
</AccessRequest>
|
||||
<RatingServiceSelectionRequest xml:lang="en-US">
|
||||
<Request>
|
||||
<RequestAction>Rate</RequestAction>
|
||||
<RequestOption>shop</RequestOption>
|
||||
</Request>
|
||||
<Shipment>
|
||||
<Shipper>
|
||||
<Address>
|
||||
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
|
||||
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
|
||||
' . ((Arr::get($shipper, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
|
||||
</Address>
|
||||
<ShipperNumber>' . $this->shipper_number . '</ShipperNumber>
|
||||
</Shipper>
|
||||
<ShipTo>
|
||||
<Address>
|
||||
<PostalCode>' . Arr::get($to, 'postal_code') . '</PostalCode>
|
||||
<CountryCode>' . Arr::get($to, 'country_code') . '</CountryCode>
|
||||
' . ((Arr::get($to, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
|
||||
</Address>
|
||||
</ShipTo>
|
||||
<ShipFrom>
|
||||
<Address>
|
||||
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
|
||||
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
|
||||
' . ((Arr::get($shipper, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
|
||||
</Address>
|
||||
</ShipFrom>
|
||||
<Service>
|
||||
<Code>' . $service_code . '</Code>
|
||||
</Service>
|
||||
<Package>
|
||||
<PackagingType>
|
||||
<Code>02</Code>
|
||||
</PackagingType>
|
||||
<Dimensions>
|
||||
<UnitOfMeasurement>
|
||||
<Code>IN</Code>
|
||||
</UnitOfMeasurement>
|
||||
<Length>' . Arr::get($dimensions, 'length') . '</Length>
|
||||
<Width>' . Arr::get($dimensions, 'width') . '</Width>
|
||||
<Height>' . Arr::get($dimensions, 'height') . '</Height>
|
||||
</Dimensions>
|
||||
<PackageWeight>
|
||||
<UnitOfMeasurement>
|
||||
<Code>LBS</Code>
|
||||
</UnitOfMeasurement>
|
||||
<Weight>' . $pounds . '</Weight>
|
||||
</PackageWeight>
|
||||
</Package>
|
||||
</Shipment>
|
||||
</RatingServiceSelectionRequest>';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function execute()
|
||||
{
|
||||
if ($this->is_prod) {
|
||||
$url = $this->url_prod;
|
||||
} else {
|
||||
$url = $this->url_dev;
|
||||
}
|
||||
|
||||
$this->response = $this->rate_request->execute($url, $this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function process()
|
||||
{
|
||||
try {
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$dom->loadXml($this->response);
|
||||
|
||||
$rate_list = @$dom->getElementsByTagName('RatedShipment');
|
||||
|
||||
if (empty($rate_list->length)) {
|
||||
throw new Exception('Unable to get UPS Rates.');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// StatsD::increment('error.shipping.get_ups_rate');
|
||||
// Kohana::$log->add(Log::ERROR, $e)->write();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
foreach ($rate_list as $rate) {
|
||||
$code = @$rate
|
||||
->getElementsByTagName('Service')->item(0)
|
||||
->getElementsByTagName('Code')->item(0)->nodeValue;
|
||||
|
||||
$name = Arr::get($this->shipping_codes['US'], $code);
|
||||
|
||||
$cost = @$rate
|
||||
->getElementsByTagName('TotalCharges')->item(0)
|
||||
->getElementsByTagName('MonetaryValue')->item(0)->nodeValue;
|
||||
|
||||
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->rates[] = array(
|
||||
'code' => $code,
|
||||
'name' => $name,
|
||||
'cost' => (int) $cost * 100,
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
namespace pdt256\Shipping\USPS;
|
||||
|
||||
use pdt256\Shipping;
|
||||
use pdt256\Shipping\Arr;
|
||||
use pdt256\Shipping\RateAdapter;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
use DOMDocument;
|
||||
use Exception;
|
||||
|
||||
class Rate extends RateAdapter
|
||||
{
|
||||
private $url_dev = 'http://production.shippingapis.com/ShippingAPI.dll';
|
||||
private $url_prod = 'http://production.shippingapis.com/ShippingAPI.dll';
|
||||
|
||||
private $username = 'XXX';
|
||||
private $password = 'XXX';
|
||||
|
||||
public $approved_codes = [
|
||||
'1',
|
||||
'4',
|
||||
];
|
||||
|
||||
private $shipping_codes = [
|
||||
'domestic' => [
|
||||
'00' => 'First-Class Mail Parcel',
|
||||
'01' => 'First-Class Mail Large Envelope',
|
||||
'02' => 'First-Class Mail Letter',
|
||||
'03' => 'First-Class Mail Postcards',
|
||||
'1' => 'Priority Mail',
|
||||
'2' => 'Express Mail Hold for Pickup',
|
||||
'3' => 'Express Mail',
|
||||
'4' => 'Parcel Post', // Standard Post
|
||||
'5' => 'Bound Printed Matter',
|
||||
'6' => 'Media Mail',
|
||||
'7' => 'Library',
|
||||
'12' => 'First-Class Postcard Stamped',
|
||||
'13' => 'Express Mail Flat-Rate Envelope',
|
||||
'16' => 'Priority Mail Flat-Rate Envelope',
|
||||
'17' => 'Priority Mail Regular Flat-Rate Box',
|
||||
'18' => 'Priority Mail Keys and IDs',
|
||||
'19' => 'First-Class Keys and IDs',
|
||||
'22' => 'Priority Mail Flat-Rate Large Box',
|
||||
'23' => 'Express Mail Sunday/Holiday',
|
||||
'25' => 'Express Mail Flat-Rate Envelope Sunday/Holiday',
|
||||
'27' => 'Express Mail Flat-Rate Envelope Hold For Pickup',
|
||||
'28' => 'Priority Mail Small Flat-Rate Box',
|
||||
],
|
||||
'international' => [
|
||||
'1' => 'Express Mail International',
|
||||
'2' => 'Priority Mail International',
|
||||
'4' => 'Global Express Guaranteed (Document and Non-document)',
|
||||
'5' => 'Global Express Guaranteed Document used',
|
||||
'6' => 'Global Express Guaranteed Non-Document Rectangular shape',
|
||||
'7' => 'Global Express Guaranteed Non-Document Non-Rectangular',
|
||||
'8' => 'Priority Mail Flat Rate Envelope',
|
||||
'9' => 'Priority Mail Flat Rate Box',
|
||||
'10' => 'Express Mail International Flat Rate Envelope',
|
||||
'11' => 'Priority Mail Flat Rate Large Box',
|
||||
'12' => 'Global Express Guaranteed Envelope',
|
||||
'13' => 'First Class Mail International Letters',
|
||||
'14' => 'First Class Mail International Flats',
|
||||
'15' => 'First Class Mail International Parcels',
|
||||
'16' => 'Priority Mail Flat Rate Small Box',
|
||||
'21' => 'Postcards',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
if (isset($options['username'])) {
|
||||
$this->username = $options['username'];
|
||||
}
|
||||
|
||||
if (isset($options['password'])) {
|
||||
$this->password = $options['password'];
|
||||
}
|
||||
|
||||
if (isset($options['username'])) {
|
||||
$this->username = $options['username'];
|
||||
}
|
||||
|
||||
if (isset($options['approved_codes'])) {
|
||||
$this->approved_codes = $options['approved_codes'];
|
||||
}
|
||||
|
||||
if (isset($options['request_adapter'])) {
|
||||
$this->set_request_adapter($options['request_adapter']);
|
||||
} else {
|
||||
$this->set_request_adapter(new RateRequest\Get());
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
$to = Arr::get($this->shipment, 'to');
|
||||
$shipper = Arr::get($this->shipment, 'from');
|
||||
$dimensions = Arr::get($this->shipment, 'dimensions');
|
||||
|
||||
// https://www.usps.com/business/web-tools-apis/rate-calculators-v1-7a.htm
|
||||
$pounds = (int) Arr::get($this->shipment, 'weight');
|
||||
$ounces = 0;
|
||||
|
||||
if ($pounds < 1) {
|
||||
throw new Exception('Weight missing');
|
||||
}
|
||||
|
||||
$this->data =
|
||||
'<RateV4Request USERID="' . $this->username . '">
|
||||
<Revision/>
|
||||
<Package ID="1">
|
||||
<Service>ALL</Service>
|
||||
<ZipOrigination>' . Arr::get($shipper, 'postal_code') . '</ZipOrigination>
|
||||
<ZipDestination>' . Arr::get($to, 'postal_code') . '</ZipDestination>
|
||||
<Pounds>' . $pounds . '</Pounds>
|
||||
<Ounces>' . $ounces . '</Ounces>
|
||||
<Container>' . Arr::get($this->shipment, 'container') . '</Container>
|
||||
<Size>' . Arr::get($this->shipment, 'size') . '</Size>
|
||||
<Width>' . Arr::get($dimensions, 'width') . '</Width>
|
||||
<Length>' . Arr::get($dimensions, 'length') . '</Length>
|
||||
<Height>' . Arr::get($dimensions, 'height') . '</Height>
|
||||
<Machinable>' . 'False' . '</Machinable>
|
||||
</Package>
|
||||
</RateV4Request>';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function execute()
|
||||
{
|
||||
if ($this->is_prod) {
|
||||
$url = $this->url_prod;
|
||||
} else {
|
||||
$url = $this->url_dev;
|
||||
}
|
||||
|
||||
$url_request = $url . '?API=RateV4&XML=' . rawurlencode($this->data);
|
||||
|
||||
$this->response = $this->rate_request->execute($url_request);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function process()
|
||||
{
|
||||
try {
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$dom->loadXml($this->response);
|
||||
|
||||
$postage_list = @$dom->getElementsByTagName('Postage');
|
||||
|
||||
if (empty($postage_list)) {
|
||||
throw new Exception('Unable to get USPS Rates.');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// StatsD::increment('error.shipping.get_usps_rate');
|
||||
// Kohana::$log->add(Log::ERROR, $e)->write();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
foreach ($postage_list as $postage) {
|
||||
$code = @$postage->getAttribute('CLASSID');
|
||||
$cost = @$postage->getElementsByTagName('Rate')->item(0)->nodeValue;
|
||||
|
||||
$name = Arr::get($this->shipping_codes['domestic'], $code);
|
||||
|
||||
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->rates[] = array(
|
||||
'code' => $code,
|
||||
'name' => $name,
|
||||
'cost' => (int) $cost * 100,
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
use pdt256\Shipping\Ship;
|
||||
use pdt256\Shipping\USPS;
|
||||
use pdt256\Shipping\UPS;
|
||||
use pdt256\Shipping\Fedex;
|
||||
use pdt256\Shipping\RateRequest;
|
||||
|
||||
class ShipTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public $shipment = [
|
||||
'weight' => 3, // lbs
|
||||
'dimensions' => [
|
||||
'width' => 9,
|
||||
'length' => 9,
|
||||
'height' => 9,
|
||||
],
|
||||
'from' => [
|
||||
'postal_code' => '90401',
|
||||
'country_code' => 'US',
|
||||
],
|
||||
'to' => [
|
||||
'postal_code' => '78703',
|
||||
'country_code' => 'US',
|
||||
'is_residential' => TRUE,
|
||||
],
|
||||
];
|
||||
|
||||
public $shipping_options = [
|
||||
'Standard Shipping' => [
|
||||
'ups' => [
|
||||
'03' => '1-5 business days',
|
||||
],
|
||||
'fedex' => [
|
||||
'FEDEX_EXPRESS_SAVER' => '1-3 business days',
|
||||
'FEDEX_GROUND' => '1-5 business days',
|
||||
'GROUND_HOME_DELIVERY' => '1-5 business days',
|
||||
],
|
||||
'usps' => [
|
||||
'1' => '1-3 business days',
|
||||
'4' => '2-8 business days',
|
||||
],
|
||||
],
|
||||
'Two-Day Shipping' => [
|
||||
'ups' => [
|
||||
'02' => '2 business days',
|
||||
],
|
||||
'fedex' => [
|
||||
'FEDEX_2_DAY' => '2 business days',
|
||||
],
|
||||
],
|
||||
'One-Day Shipping' => [
|
||||
'ups' => [
|
||||
'01' => 'next business day 10:30am',
|
||||
'13' => 'next business day by 3pm',
|
||||
'14' => 'next business day by 8am',
|
||||
],
|
||||
'fedex' => [
|
||||
'STANDARD_OVERNIGHT' => 'overnight',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
private function getUSPSOptions()
|
||||
{
|
||||
$ship = Ship::factory($this->shipping_options);
|
||||
$approved_codes = $ship->get_approved_codes('usps');
|
||||
|
||||
return [
|
||||
'prod' => FALSE,
|
||||
'username' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'shipment' => array_merge($this->shipment, [
|
||||
'size' => 'LARGE',
|
||||
'container' => 'RECTANGULAR',
|
||||
]),
|
||||
'approved_codes' => $approved_codes,
|
||||
'request_adapter' => new RateRequest\StubUSPS(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getUPSOptions()
|
||||
{
|
||||
$ship = Ship::factory($this->shipping_options);
|
||||
$approved_codes = $ship->get_approved_codes('ups');
|
||||
|
||||
return [
|
||||
'prod' => FALSE,
|
||||
'access_key' => 'XXXX',
|
||||
'user_id' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'shipper_number' => 'XXXX',
|
||||
'shipment' => $this->shipment,
|
||||
'approved_codes' => $approved_codes,
|
||||
'request_adapter' => new RateRequest\StubUPS(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getFedexOptions()
|
||||
{
|
||||
$ship = Ship::factory($this->shipping_options);
|
||||
$approved_codes = $ship->get_approved_codes('fedex');
|
||||
|
||||
return [
|
||||
'prod' => FALSE,
|
||||
'key' => 'XXXX',
|
||||
'password' => 'XXXX',
|
||||
'account_number' => 'XXXX',
|
||||
'meter_number' => 'XXXX',
|
||||
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
|
||||
'shipment' => array_merge($this->shipment, [
|
||||
'packaging_type' => 'YOUR_PACKAGING',
|
||||
]),
|
||||
'approved_codes' => $approved_codes,
|
||||
'request_adapter' => new RateRequest\StubFedex(),
|
||||
];
|
||||
}
|
||||
|
||||
public function testUSPSRate()
|
||||
{
|
||||
$usps = new USPS\Rate($this->getUSPSOptions());
|
||||
$usps_rates = $usps->get_rates();
|
||||
|
||||
$this->assertEquals(json_encode([
|
||||
1 => [
|
||||
'code' => '4',
|
||||
'name' => 'Parcel Post',
|
||||
'cost' => 1000,
|
||||
],
|
||||
0 => [
|
||||
'code' => '1',
|
||||
'name' => 'Priority Mail',
|
||||
'cost' => 1200,
|
||||
],
|
||||
]), json_encode($usps_rates));
|
||||
}
|
||||
|
||||
public function testUPSRate()
|
||||
{
|
||||
$ups = new UPS\Rate($this->getUPSOptions());
|
||||
$ups_rates = $ups->get_rates();
|
||||
|
||||
$this->assertEquals(json_encode([
|
||||
0 => [
|
||||
'code' => '03',
|
||||
'name' => 'UPS Ground',
|
||||
'cost' => 1900,
|
||||
],
|
||||
1 => [
|
||||
'code' => '02',
|
||||
'name' => 'UPS 2nd Day Air',
|
||||
'cost' => 4900,
|
||||
],
|
||||
2 => [
|
||||
'code' => '13',
|
||||
'name' => 'UPS Next Day Air Saver',
|
||||
'cost' => 8900,
|
||||
],
|
||||
3 => [
|
||||
'code' => '01',
|
||||
'name' => 'UPS Next Day Air',
|
||||
'cost' => 9300,
|
||||
],
|
||||
]), json_encode($ups_rates));
|
||||
}
|
||||
|
||||
public function testFedexRate()
|
||||
{
|
||||
$fedex = new Fedex\Rate($this->getFedexOptions());
|
||||
$fedex_rates = $fedex->get_rates();
|
||||
|
||||
$this->assertEquals(json_encode([
|
||||
3 => [
|
||||
'code' => 'GROUND_HOME_DELIVERY',
|
||||
'name' => 'Ground Home Delivery',
|
||||
'cost' => 1600,
|
||||
'delivery_ts' => NULL,
|
||||
'transit_time' => 'THREE_DAYS',
|
||||
],
|
||||
2 => [
|
||||
'code' => 'FEDEX_EXPRESS_SAVER',
|
||||
'name' => 'Fedex Express Saver',
|
||||
'cost' => 2900,
|
||||
'delivery_ts' => '2014-09-30T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
],
|
||||
1 => [
|
||||
'code' => 'FEDEX_2_DAY',
|
||||
'name' => 'Fedex 2 Day',
|
||||
'cost' => 4000,
|
||||
'delivery_ts' => '2014-09-29T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
],
|
||||
0 => [
|
||||
'code' => 'STANDARD_OVERNIGHT',
|
||||
'name' => 'Standard Overnight',
|
||||
'cost' => 7800,
|
||||
'delivery_ts' => '2014-09-26T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
],
|
||||
]), json_encode($fedex_rates));
|
||||
}
|
||||
|
||||
public function testDisplayOptions()
|
||||
{
|
||||
$rates = [];
|
||||
|
||||
$usps = new USPS\Rate($this->getUSPSOptions());
|
||||
$rates['usps'] = $usps->get_rates();
|
||||
|
||||
$ups = new UPS\Rate($this->getUPSOptions());
|
||||
$rates['ups'] = $ups->get_rates();
|
||||
|
||||
$fedex = new Fedex\Rate($this->getFedexOptions());
|
||||
$rates['fedex'] = $fedex->get_rates();
|
||||
|
||||
$ship = Ship::factory($this->shipping_options);
|
||||
$display_rates = $ship->get_display_rates($rates);
|
||||
|
||||
$this->assertEquals(json_encode([
|
||||
'Standard Shipping' => [
|
||||
0 => [
|
||||
'code' => '4',
|
||||
'name' => 'Parcel Post',
|
||||
'cost' => 1000,
|
||||
'carrier' => 'usps',
|
||||
],
|
||||
],
|
||||
'Two-Day Shipping' => [
|
||||
0 => [
|
||||
'code' => 'FEDEX_2_DAY',
|
||||
'name' => 'Fedex 2 Day',
|
||||
'cost' => 4000,
|
||||
'delivery_ts' => '2014-09-29T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
'carrier' => 'fedex',
|
||||
],
|
||||
],
|
||||
'One-Day Shipping' => [
|
||||
0 => [
|
||||
'code' => 'STANDARD_OVERNIGHT',
|
||||
'name' => 'Standard Overnight',
|
||||
'cost' => 7800,
|
||||
'delivery_ts' => '2014-09-26T20:00:00',
|
||||
'transit_time' => NULL,
|
||||
'carrier' => 'fedex',
|
||||
],
|
||||
],
|
||||
]), json_encode($display_rates));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUSPSRateMissingTo()
|
||||
{
|
||||
$usps_options = $this->getUSPSOptions();
|
||||
unset($usps_options['shipment']['to']);
|
||||
|
||||
$usps = new USPS\Rate($usps_options);
|
||||
$usps_rates = $usps->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUSPSRateMissingFrom()
|
||||
{
|
||||
$usps_options = $this->getUSPSOptions();
|
||||
unset($usps_options['shipment']['from']);
|
||||
|
||||
$usps = new USPS\Rate($usps_options);
|
||||
$usps_rates = $usps->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUSPSRateMissingDimensions()
|
||||
{
|
||||
$usps_options = $this->getUSPSOptions();
|
||||
unset($usps_options['shipment']['dimensions']);
|
||||
|
||||
$usps = new USPS\Rate($usps_options);
|
||||
$usps_rates = $usps->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUPSRateMissingTo()
|
||||
{
|
||||
$ups_options = $this->getUPSOptions();
|
||||
unset($ups_options['shipment']['to']);
|
||||
|
||||
$ups = new UPS\Rate($ups_options);
|
||||
$ups_rates = $ups->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUPSRateMissingFrom()
|
||||
{
|
||||
$ups_options = $this->getUPSOptions();
|
||||
unset($ups_options['shipment']['from']);
|
||||
|
||||
$ups = new UPS\Rate($ups_options);
|
||||
$ups_rates = $ups->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testUPSRateMissingDimensions()
|
||||
{
|
||||
$ups_options = $this->getUPSOptions();
|
||||
unset($ups_options['shipment']['dimensions']);
|
||||
|
||||
$ups = new UPS\Rate($ups_options);
|
||||
$ups_rates = $ups->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testFedexRateMissingTo()
|
||||
{
|
||||
$fedex_options = $this->getFedexOptions();
|
||||
unset($fedex_options['shipment']['to']);
|
||||
|
||||
$fedex = new Fedex\Rate($fedex_options);
|
||||
$fedex_rates = $fedex->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testFedexRateMissingFrom()
|
||||
{
|
||||
$fedex_options = $this->getFedexOptions();
|
||||
unset($fedex_options['shipment']['from']);
|
||||
|
||||
$fedex = new Fedex\Rate($fedex_options);
|
||||
$fedex_rates = $fedex->get_rates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testFedexRateMissingDimensions()
|
||||
{
|
||||
$fedex_options = $this->getFedexOptions();
|
||||
unset($fedex_options['shipment']['dimensions']);
|
||||
|
||||
$fedex = new Fedex\Rate($fedex_options);
|
||||
$fedex_rates = $fedex->get_rates();
|
||||
}
|
||||
|
||||
// // Readme Examples:
|
||||
// public function testUSPSReadmeExample()
|
||||
// {
|
||||
// $usps = new USPS\Rate([
|
||||
// 'prod' => FALSE,
|
||||
// 'username' => 'XXXX',
|
||||
// 'password' => 'XXXX',
|
||||
// 'shipment' => array_merge($this->shipment, [
|
||||
// 'size' => 'LARGE',
|
||||
// 'container' => 'RECTANGULAR',
|
||||
// ]),
|
||||
// 'approved_codes' => [
|
||||
// '1', // 1-3 business days
|
||||
// '4', // 2-8 business days
|
||||
// ],
|
||||
// 'request_adapter' => new RateRequest\StubUSPS(),
|
||||
// ]);
|
||||
//
|
||||
// $usps_rates = $usps->get_rates();
|
||||
// var_export($usps_rates);
|
||||
// }
|
||||
//
|
||||
// public function testUPSReadmeExample()
|
||||
// {
|
||||
// $ups = new UPS\Rate([
|
||||
// 'prod' => FALSE,
|
||||
// 'shipment' => $this->shipment,
|
||||
// 'approved_codes' => [
|
||||
// '03', // 1-5 business days
|
||||
// '02', // 2 business days
|
||||
// '01', // next business day 10:30am
|
||||
// '13', // next business day by 3pm
|
||||
// '14', // next business day by 8am
|
||||
// ],
|
||||
// 'request_adapter' => new RateRequest\StubUPS(),
|
||||
// ]);
|
||||
//
|
||||
// $ups_rates = $ups->get_rates();
|
||||
// var_export($ups_rates);
|
||||
// }
|
||||
//
|
||||
// public function testFedexReadmeExample()
|
||||
// {
|
||||
// $fedex = new Fedex\Rate([
|
||||
// 'prod' => FALSE,
|
||||
// 'key' => 'XXXX',
|
||||
// 'password' => 'XXXX',
|
||||
// 'account_number' => 'XXXX',
|
||||
// 'meter_number' => 'XXXX',
|
||||
// 'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
|
||||
// 'shipment' => array_merge($this->shipment, [
|
||||
// 'packaging_type' => 'YOUR_PACKAGING',
|
||||
// ]),
|
||||
// 'approved_codes' => [
|
||||
// 'FEDEX_EXPRESS_SAVER', // 1-3 business days
|
||||
// 'FEDEX_GROUND', // 1-5 business days
|
||||
// 'GROUND_HOME_DELIVERY', // 1-5 business days
|
||||
// 'FEDEX_2_DAY', // 2 business days
|
||||
// 'STANDARD_OVERNIGHT', // overnight
|
||||
// ],
|
||||
// 'request_adapter' => new RateRequest\StubFedex(),
|
||||
// ]);
|
||||
//
|
||||
// $fedex_rates = $fedex->get_rates();
|
||||
// var_export($fedex_rates);
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue