Migrating code to PSR2. Adding optional support for live API calls in unit tests. Adding rounding error bug fix. Adding more tests to get to 85% coverage. Adding phpstorm project. Bumping master for v1.0.x target.

pull/11/head
Jamie Isaacs 9 years ago
parent 01ddc321ce
commit ccf447f755

11
.gitignore vendored

@ -1,4 +1,9 @@
vendor/
.idea/
vendor
composer.lock
coverage_report
.idea/*.iws
.idea/*.iml
.idea/workspace.xml
.idea/tasks.xml
.DS_Store
live_phpunit.sh

@ -0,0 +1 @@
shipping

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="PhpCSValidationInspection" enabled="true" level="ERROR" enabled_by_default="true">
<option name="CODING_STANDARD" value="PSR2" />
<option name="WARNING_HIGHLIGHT_LEVEL_NAME" value="ERROR" />
</inspection_tool>
</profile>
</component>

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/shipping.iml" filepath="$PROJECT_DIR$/.idea/shipping.iml" />
</modules>
</component>
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings load_method="CUSTOM_LOADER" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" phpunit_phar_path="" />
</phpunit_settings>
</component>
</project>

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -0,0 +1,10 @@
language: php
php:
- 5.4
before_script:
- composer install --prefer-dist
script:
- vendor/bin/phpcs --standard=PSR2 src/ tests/
- vendor/bin/phpunit

@ -1,9 +1,22 @@
## PHP Shipping API
[![Total Downloads](https://poser.pugx.org/pdt256/shipping/downloads.svg)](https://packagist.org/packages/pdt256/shipping) [![Latest Stable Version](https://poser.pugx.org/pdt256/shipping/v/stable.svg)](https://packagist.org/packages/pdt256/shipping) [![Latest Unstable Version](https://poser.pugx.org/pdt256/shipping/v/unstable.svg)](https://packagist.org/packages/pdt256/shipping) [![License](https://poser.pugx.org/pdt256/shipping/license.svg)](https://packagist.org/packages/pdt256/shipping)
[![Test Coverage](http://img.shields.io/badge/coverage-85%25-green.svg)]
[![Build Status](https://travis-ci.org/pdt256/shipping.svg?branch=master)](https://travis-ci.org/pdt256/shipping)
[![Downloads](https://img.shields.io/packagist/dt/pdt256/shipping.svg)](https://packagist.org/packages/pdt256/shipping)
[![License](https://img.shields.io/packagist/l/pdt256/shipping.svg)](https://github.com/pdt256/shipping/blob/master/LICENSE.txt)
A shipping rate wrapper for USPS, UPS, and Fedex.
## Introduction
This is a PHP shipping package that wraps API calls to UPS, FedEx, and USPS for shipping rates.
Multiple packages can be added to get additional rates.
All code (including tests) conform to the PSR-2 coding standards.
The namespace and autoloader are using the PSR-4 standard.
All pull requests are processed by Travis CI to conform to PSR-2 and to verify all unit tests pass.
## Installation
Add the following lines to your ``composer.json`` file.
@ -11,7 +24,7 @@ Add the following lines to your ``composer.json`` file.
```JSON
{
"require": {
"pdt256/shipping": "dev-master"
"pdt256/shipping": "1.0.*@dev"
}
}
```
@ -23,15 +36,20 @@ Create a shipment object:
```php
$shipment = new Shipment;
$shipment
->setFromIsResidential(false)
->setFromStateProvinceCode('IN')
->setFromPostalCode('46205')
->setFromCountryCode('US')
->setToIsResidential(true);
->setToPostalCode('20101')
->setToCountryCode('US')
->setToResidential(true);
$package = new Package;
$package->setLength(12)->setWidth(4)->setHeight(3)->setWeight(3);
$package
->setLength(12)
->setWidth(4)
->setHeight(3)
->setWeight(3);
$shipment->addPackage($package);
```
@ -44,7 +62,7 @@ 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.
```php
'request_adapter' => new RateRequest\StubUPS(),
'requestAdapter' => new RateRequest\StubUPS(),
```
```php
@ -53,22 +71,22 @@ use pdt256\Shipping\RateRequest;
$ups = new UPS\Rate([
'prod' => FALSE,
'access_key' => 'XXXX',
'user_id' => 'XXXX',
'accessKey' => 'XXXX',
'userId' => 'XXXX',
'password' => 'XXXX',
'shipper_number' => 'XXXX',
'shipperNumber' => 'XXXX',
'shipment' => $shipment,
'approved_codes' => [
'approvedCodes' => [
'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(),
'requestAdapter' => new RateRequest\StubUPS(),
]);
$ups_rates = $ups->get_rates();
$upsRates = $ups->getRates();
```
Output array sorted by cost: (in cents)
@ -83,9 +101,9 @@ array(4) {
string(10) "UPS Ground"
protected $cost =>
int(1900)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(3) "ups"
@ -98,9 +116,9 @@ array(4) {
string(15) "UPS 2nd Day Air"
protected $cost =>
int(4900)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(3) "ups"
@ -113,9 +131,9 @@ array(4) {
string(22) "UPS Next Day Air Saver"
protected $cost =>
int(8900)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(3) "ups"
@ -128,9 +146,9 @@ array(4) {
string(16) "UPS Next Day Air"
protected $cost =>
int(9300)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(3) "ups"
@ -149,14 +167,14 @@ $usps = new USPS\Rate([
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => $shipment,
'approved_codes' => [
'approvedCodes' => [
'1', // 1-3 business days
'4', // 2-8 business days
],
'request_adapter' => new RateRequest\StubUSPS(),
'requestAdapter' => new RateRequest\StubUSPS(),
]);
$usps_rates = $usps->get_rates();
$uspsRates = $usps->getRates();
```
Output array sorted by cost: (in cents)
@ -171,9 +189,9 @@ array(2) {
string(11) "Parcel Post"
protected $cost =>
int(1001)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(4) "usps"
@ -186,9 +204,9 @@ array(2) {
string(13) "Priority Mail"
protected $cost =>
int(1220)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(4) "usps"
@ -206,21 +224,21 @@ $fedex = new Fedex\Rate([
'prod' => FALSE,
'key' => 'XXXX',
'password' => 'XXXX',
'account_number' => 'XXXX',
'meter_number' => 'XXXX',
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'accountNumber' => 'XXXX',
'meterNumber' => 'XXXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $shipment,
'approved_codes' => [
'approvedCodes' => [
'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(),
'requestAdapter' => new RateRequest\StubFedex(),
]);
$fedex_rates = $fedex->get_rates();
$fedexRates = $fedex->getRates();
```
Output array sorted by cost: (in cents)
@ -235,9 +253,9 @@ array(4) {
string(20) "Ground Home Delivery"
protected $cost =>
int(1600)
protected $transit_time =>
protected $transitTime =>
string(10) "THREE_DAYS"
protected $delivery_ts =>
protected $deliveryTime =>
NULL
protected $carrier =>
string(5) "fedex"
@ -250,9 +268,9 @@ array(4) {
string(19) "Fedex Express Saver"
protected $cost =>
int(2900)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
class Carbon\Carbon#23 (3) {
public $date =>
string(26) "2014-09-30 20:00:00.000000"
@ -272,9 +290,9 @@ array(4) {
string(11) "Fedex 2 Day"
protected $cost =>
int(4000)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
class Carbon\Carbon#26 (3) {
public $date =>
string(26) "2014-09-29 20:00:00.000000"
@ -294,9 +312,9 @@ array(4) {
string(18) "Standard Overnight"
protected $cost =>
int(7800)
protected $transit_time =>
protected $transitTime =>
NULL
protected $delivery_ts =>
protected $deliveryTime =>
class Carbon\Carbon#58 (3) {
public $date =>
string(26) "2014-09-26 20:00:00.000000"
@ -311,6 +329,31 @@ array(4) {
}
```
## Unit Tests:
```bash
vendor/bin/phpunit
```
### With Code Coverage:
```bash
vendor/bin/phpunit --coverage-text --coverage-html coverage_report
```
### With Live API Tests:
```bash
./live_phpunit.sh
```
## Run Coding Standards Test:
```bash
vendor/bin/phpcs --standard=PSR2 src/ tests/
```
### License
The MIT License (MIT)

@ -9,8 +9,17 @@
"email": "pdt256@gmail.com"
}
],
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"require-dev": {
"phpunit/phpunit": "4.0.*"
"phpunit/phpunit": "4.0.*",
"squizlabs/php_codesniffer": "1.5.5"
},
"require": {
"php": ">=5.4"
},
"autoload": {
"psr-4": {

@ -0,0 +1,16 @@
#!/bin/bash
export USPS_USERNAME='XXXXXXXXXXXX'
export USPS_PASSWORD='XXXXXXXXXXXX'
export UPS_ACCESS_KEY='XXXXXXXXXXXXXXXX'
export UPS_USER_ID='XXXXXX'
export UPS_PASSWORD='XXXX'
export UPS_SHIPPER_NUMBER='XXXXXX'
export FEDEX_KEY='XXXXXXXXXXXXXXXX'
export FEDEX_PASSWORD='XXXXXXXXXXXXXXXXXXXXXXXXX'
export FEDEX_ACCOUNT_NUMBER='XXXXXXXXX'
export FEDEX_METER_NUMBER='XXXXXXXXX'
vendor/bin/phpunit

@ -3,7 +3,7 @@ namespace pdt256\Shipping;
class Arr
{
public static function get($array, $key, $default = NULL)
public static function get($array, $key, $default = null)
{
return isset($array[$key]) ? $array[$key] : $default;
}

@ -12,16 +12,16 @@ 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 $urlDev = 'https://gatewaybeta.fedex.com/web-services/';
private $urlProd = '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';
private $accountNumber = 'XXX';
private $meterNumber = 'XXX';
private $dropOffType = 'BUSINESS_SERVICE_CENTER';
public $approved_codes = [
public $approvedCodes = [
'PRIORITY_OVERNIGHT',
'FEDEX_2_DAY',
'FEDEX_EXPRESS_SAVER',
@ -29,7 +29,7 @@ class Rate extends RateAdapter
'GROUND_HOME_DELIVERY',
];
private $shipping_codes = [
private $shippingCodes = [
'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => 'Europe First International Priority',
'FEDEX_1_DAY_FREIGHT' => 'Fedex 1 Day Freight',
'FEDEX_2_DAY' => 'Fedex 2 Day',
@ -57,35 +57,14 @@ class Rate extends RateAdapter
{
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'];
}
$this->key = Arr::get($options, 'key');
$this->password = Arr::get($options, 'password');
$this->accountNumber = Arr::get($options, 'accountNumber');
$this->meterNumber = Arr::get($options, 'meterNumber');
$this->approvedCodes = Arr::get($options, 'approvedCodes');
$this->dropOffType = Arr::get($options, 'dropOffType');
if (isset($options['request_adapter'])) {
$this->set_request_adapter($options['request_adapter']);
} else {
$this->set_request_adapter(new RateRequest\Post());
}
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Post()));
}
protected function prepare()
@ -100,90 +79,99 @@ class Rate extends RateAdapter
}
// 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
// http://www.fedex.com/us/developer/product/WebServices/MyWebHelp_August2010/Content/
// Proprietary_Developer_Guide/Rate_Services_conditionalized.htm
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
foreach ($this->shipment->getPackages() as $package) {
$sequence_number++;
$packages .= '<RequestedPackageLineItems>
<SequenceNumber>' . $sequence_number . '</SequenceNumber>
<GroupPackageCount>1</GroupPackageCount>
<Weight>
<Units>LB</Units>
<Value>' . $p->getWeight() . '</Value>
</Weight>
<Dimensions>
<Length>' . $p->getLength() . '</Length>
<Width>' . $p->getWidth() . '</Width>
<Height>' . $p->getHeight() . '</Height>
<Units>IN</Units>
</Dimensions>
</RequestedPackageLineItems>';
}
$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>YOUR_PACKAGING</PackagingType>
<Shipper>
<Address>
<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<Residential>1</Residential>' : '') . '
</Address>
</Shipper>
<Recipient>
<Address>
<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>
' . (($this->shipment->isToResidential()) ? '<Residential>1</Residential>' : '') . '
</Address>
</Recipient>
<RateRequestTypes>LIST</RateRequestTypes>
<PackageCount>' . $this->shipment->packageCount() . '</PackageCount>
' . $packages . '
</RequestedShipment>
</RateRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
$packages .=
'<RequestedPackageLineItems>' .
'<SequenceNumber>' . $sequence_number . '</SequenceNumber>' .
'<GroupPackageCount>1</GroupPackageCount>' .
'<Weight>' .
'<Units>LB</Units>' .
'<Value>' . $package->getWeight() . '</Value>' .
'</Weight>' .
'<Dimensions>' .
'<Length>' . $package->getLength() . '</Length>' .
'<Width>' . $package->getWidth() . '</Width>' .
'<Height>' . $package->getHeight() . '</Height>' .
'<Units>IN</Units>' .
'</Dimensions>' .
'</RequestedPackageLineItems>';
}
$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->accountNumber . '</AccountNumber>' .
'<MeterNumber>' . $this->meterNumber . '</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->dropOffType . '</DropoffType>' .
'<PackagingType>YOUR_PACKAGING</PackagingType>' .
'<Shipper>' .
'<Address>' .
'<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>' .
'<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>' .
(
$this->shipment->getFromIsResidential() ?
'<Residential>1</Residential>' :
''
) .
'</Address>' .
'</Shipper>' .
'<Recipient>' .
'<Address>' .
'<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>' .
'<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>' .
(
$this->shipment->getToIsResidential() ?
'<Residential>1</Residential>' :
''
) .
'</Address>' .
'</Recipient>' .
'<RateRequestTypes>LIST</RateRequestTypes>' .
'<PackageCount>' . $this->shipment->packageCount() . '</PackageCount>' .
$packages .
'</RequestedShipment>' .
'</RateRequest>' .
'</SOAP-ENV:Body>' .
'</SOAP-ENV:Envelope>';
return $this;
}
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->url_dev;
$url = $this->urlDev;
}
$this->response = $this->rate_request->execute($url, $this->data);
$this->response = $this->rateRequest->execute($url, $this->data);
return $this;
}
@ -207,11 +195,11 @@ class Rate extends RateAdapter
foreach ($rate_reply as $rate) {
$code = $rate->getElementsByTagName('ServiceType')->item(0)->nodeValue;
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
if (! empty($this->approvedCodes) && ! in_array($code, $this->approvedCodes)) {
continue;
}
$name = Arr::get($this->shipping_codes, $code);
$name = Arr::get($this->shippingCodes, $code);
$delivery_ts = @$rate->getElementsByTagName('DeliveryTimestamp')->item(0)->nodeValue;
$transit_time = @$rate->getElementsByTagName('TransitTime')->item(0)->nodeValue;
@ -227,7 +215,7 @@ class Rate extends RateAdapter
->setCarrier('fedex')
->setCode($code)
->setName($name)
->setCost((int) $cost * 100)
->setCost((int) ($cost * 100))
->setTransitTime($transit_time);
if ($delivery_ts) {
$quote->setDeliveryEstimate(new DateTime($delivery_ts));

@ -7,10 +7,18 @@ class Quote
protected $code;
protected $name;
protected $cost;
protected $transit_time;
protected $delivery_ts;
protected $transitTime;
protected $deliveryEstimate;
protected $carrier;
public function __construct($carrier = null, $code = null, $name = null, $cost = null)
{
$this->setCarrier($carrier);
$this->setCode($code);
$this->setName($name);
$this->setCost($cost);
}
/**
* @return mixed
*/
@ -25,7 +33,7 @@ class Quote
*/
public function setCarrier($carrier)
{
$this->carrier = $carrier;
$this->carrier = (string) $carrier;
return $this;
}
@ -43,7 +51,7 @@ class Quote
*/
public function setCode($code)
{
$this->code = $code;
$this->code = (string) $code;
return $this;
}
@ -61,7 +69,7 @@ class Quote
*/
public function setName($name)
{
$this->name = $name;
$this->name = (string) $name;
return $this;
}
@ -81,7 +89,7 @@ class Quote
*/
public function setCost($cost)
{
$this->cost = $cost;
$this->cost = (int) $cost;
return $this;
}
@ -90,16 +98,16 @@ class Quote
*/
public function getTransitTime()
{
return $this->transit_time;
return $this->transitTime;
}
/**
* @param mixed $transit_time
* @param mixed $transitTime
* @return $this
*/
public function setTransitTime($transit_time)
public function setTransitTime($transitTime)
{
$this->transit_time = $transit_time;
$this->transitTime = $transitTime;
return $this;
}
@ -108,16 +116,16 @@ class Quote
*/
public function getDeliveryEstimate()
{
return $this->delivery_ts;
return $this->deliveryEstimate;
}
/**
* @param DateTime $estimate
* @param DateTime $deliveryEstimate
* @return $this
*/
public function setDeliveryEstimate(DateTime $estimate)
public function setDeliveryEstimate(DateTime $deliveryEstimate)
{
$this->delivery_ts = $estimate;
$this->deliveryEstimate = $deliveryEstimate;
return $this;
}
}

@ -5,48 +5,57 @@ use Exception;
abstract class RateAdapter
{
protected $is_prod = FALSE;
protected $isProduction;
/** @var Shipment */
protected $shipment;
protected $data;
protected $response;
protected $rates = [];
protected $rates;
protected $rate_request;
/** @var @var RateRequest\Adapter */
protected $rateRequest;
abstract protected function prepare(); // Prepare XML
abstract protected function execute(); // Curl Request
abstract protected function process(); // Convert to shipping rates array
/**
* Prepare XML
*/
abstract protected function prepare();
/**
* Curl Request
*/
abstract protected function execute();
/**
* Convert to shipping rates array
*/
abstract protected function process();
public function __construct($options = [])
{
if (isset($options['prod'])) {
$this->is_prod = (bool) $options['prod'];
}
$this->rates = [];
if (isset($options['shipment'])) {
$this->shipment = $options['shipment'];
}
$this->isProduction = (bool) Arr::get($options, 'prod', false);
$this->shipment = Arr::get($options, 'shipment');
}
public function set_request_adapter(RateRequest\Adapter $rate_request)
public function setRequestAdapter(RateRequest\Adapter $rateRequest)
{
$this->rate_request = $rate_request;
$this->rateRequest = $rateRequest;
}
public function get_rates()
public function getRates()
{
$this
->prepare()
->execute()
->process()
->sort_by_cost();
->sortByCost();
return array_values($this->rates);
}
protected function sort_by_cost()
protected function sortByCost()
{
uasort($this->rates, create_function('$a, $b', 'return ($a->getCost() > $b->getCost());'));
}

@ -3,8 +3,8 @@ namespace pdt256\Shipping\RateRequest;
abstract class Adapter
{
protected $curl_connect_timeout_ms = 1000; // milliseconds
protected $curl_dl_timeout = 11; // seconds
protected $curlConnectTimeoutInMilliseconds = 1000;
protected $curlDownloadTimeoutInSeconds = 11;
abstract public function execute($url, $data = NULL);
abstract public function execute($url, $data = null);
}

File diff suppressed because it is too large Load Diff

@ -3,17 +3,18 @@ namespace pdt256\Shipping\RateRequest;
class Get extends Adapter
{
public function execute($url, $data = NULL)
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_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);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->curlConnectTimeoutInMilliseconds);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curlDownloadTimeoutInSeconds);
$response = curl_exec($ch);
curl_close($ch);
if ($response === false) {

@ -3,18 +3,19 @@ namespace pdt256\Shipping\RateRequest;
class Post extends Adapter
{
public function execute($url, $data = NULL)
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_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_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);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->curlConnectTimeoutInMilliseconds);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curlDownloadTimeoutInSeconds);
$response = curl_exec($ch);
if ($response === false) {
throw new RequestException(curl_error($ch));

@ -1,6 +1,6 @@
<?php
namespace pdt256\Shipping\RateRequest;
class RequestException extends \Exception {}
class RequestException extends \Exception
{
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -3,22 +3,19 @@ namespace pdt256\Shipping\RateRequest;
class StubUSPS extends Adapter
{
private $artificial_delay = 0;
private $artificialDelay = 0;
public function __construct($artificial_delay = 0)
{
$this->artificial_delay = $artificial_delay;
$this->artificialDelay = $artificial_delay;
}
public function execute($url, $data = NULL)
public function execute($url, $data = null)
{
if ($this->artificial_delay > 0) {
sleep($this->artificial_delay);
if ($this->artificialDelay > 0) {
sleep($this->artificialDelay);
}
$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&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt;</MailService><Rate>42.25</Rate></Postage><Postage CLASSID="2"><MailService>Priority Mail Express 2-Day&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt; Hold For Pickup</MailService><Rate>42.25</Rate></Postage><Postage CLASSID="1"><MailService>Priority Mail 2-Day&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt;</MailService><Rate>12.20</Rate></Postage><Postage CLASSID="4"><MailService>Standard Post&amp;lt;sup&amp;gt;&amp;#174;&amp;lt;/sup&amp;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;
return file_get_contents(__DIR__ . '/USPSResponse.xml');
}
}

@ -0,0 +1,247 @@
<?xml version="1.0"?>
<RatingServiceSelectionResponse>
<Response>
<ResponseStatusCode>1</ResponseStatusCode>
<ResponseStatusDescription>Success</ResponseStatusDescription>
</Response>
<RatedShipment>
<Service>
<Code>03</Code>
</Service>
<RatedShipmentWarning>Your invoice may vary from the displayed reference rates</RatedShipmentWarning>
<RatedShipmentWarning>Missing / Invalid Shipper Number. Returned rates are Retail Rates.</RatedShipmentWarning>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>3.0</Weight>
</BillingWeight>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>19.10</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>19.10</MonetaryValue>
</TotalCharges>
<GuaranteedDaysToDelivery/>
<ScheduledDeliveryTime/>
<RatedPackage>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>19.10</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>19.10</MonetaryValue>
</TotalCharges>
<Weight>3.0</Weight>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>3.0</Weight>
</BillingWeight>
</RatedPackage>
</RatedShipment>
<RatedShipment>
<Service>
<Code>12</Code>
</Service>
<RatedShipmentWarning>Your invoice may vary from the displayed reference rates</RatedShipmentWarning>
<RatedShipmentWarning>Missing / Invalid Shipper Number. Returned rates are Retail Rates.</RatedShipmentWarning>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>37.18</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>37.18</MonetaryValue>
</TotalCharges>
<GuaranteedDaysToDelivery>3</GuaranteedDaysToDelivery>
<ScheduledDeliveryTime/>
<RatedPackage>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>37.18</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>37.18</MonetaryValue>
</TotalCharges>
<Weight>3.0</Weight>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
</RatedPackage>
</RatedShipment>
<RatedShipment>
<Service>
<Code>02</Code>
</Service>
<RatedShipmentWarning>Your invoice may vary from the displayed reference rates</RatedShipmentWarning>
<RatedShipmentWarning>Missing / Invalid Shipper Number. Returned rates are Retail Rates.</RatedShipmentWarning>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>49.23</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>49.23</MonetaryValue>
</TotalCharges>
<GuaranteedDaysToDelivery>2</GuaranteedDaysToDelivery>
<ScheduledDeliveryTime/>
<RatedPackage>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>49.23</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>49.23</MonetaryValue>
</TotalCharges>
<Weight>3.0</Weight>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
</RatedPackage>
</RatedShipment>
<RatedShipment>
<Service>
<Code>13</Code>
</Service>
<RatedShipmentWarning>Your invoice may vary from the displayed reference rates</RatedShipmentWarning>
<RatedShipmentWarning>Missing / Invalid Shipper Number. Returned rates are Retail Rates.</RatedShipmentWarning>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>89.54</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>89.54</MonetaryValue>
</TotalCharges>
<GuaranteedDaysToDelivery>1</GuaranteedDaysToDelivery>
<ScheduledDeliveryTime/>
<RatedPackage>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>89.54</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>89.54</MonetaryValue>
</TotalCharges>
<Weight>3.0</Weight>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
</RatedPackage>
</RatedShipment>
<RatedShipment>
<Service>
<Code>01</Code>
</Service>
<RatedShipmentWarning>Your invoice may vary from the displayed reference rates</RatedShipmentWarning>
<RatedShipmentWarning>Missing / Invalid Shipper Number. Returned rates are Retail Rates.</RatedShipmentWarning>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>93.28</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>93.28</MonetaryValue>
</TotalCharges>
<GuaranteedDaysToDelivery>1</GuaranteedDaysToDelivery>
<ScheduledDeliveryTime>12:00 Noon</ScheduledDeliveryTime>
<RatedPackage>
<TransportationCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>93.28</MonetaryValue>
</TransportationCharges>
<ServiceOptionsCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>0.00</MonetaryValue>
</ServiceOptionsCharges>
<TotalCharges>
<CurrencyCode>USD</CurrencyCode>
<MonetaryValue>93.28</MonetaryValue>
</TotalCharges>
<Weight>3.0</Weight>
<BillingWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>5.0</Weight>
</BillingWeight>
</RatedPackage>
</RatedShipment>
</RatingServiceSelectionResponse>

@ -0,0 +1,37 @@
<?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&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt;</MailService>
<Rate>42.25</Rate>
</Postage>
<Postage CLASSID="2">
<MailService>Priority Mail Express 2-Day&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt; Hold For Pickup
</MailService>
<Rate>42.25</Rate>
</Postage>
<Postage CLASSID="1">
<MailService>Priority Mail 2-Day&amp;lt;sup&amp;gt;&amp;#8482;&amp;lt;/sup&amp;gt;</MailService>
<Rate>12.20</Rate>
</Postage>
<Postage CLASSID="4">
<MailService>Standard Post&amp;lt;sup&amp;gt;&amp;#174;&amp;lt;/sup&amp;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>

@ -42,56 +42,60 @@ class Ship
{
$object = new self();
if ( ! empty($shipping_options)) {
if (!empty($shipping_options)) {
$object->shipping_options = $shipping_options;
}
return $object;
}
public function get_approved_codes($carrier = NULL) {
$approved_codes = [];
/**
* @return array
*/
public function getApprovedCodes($carrier = null)
{
$approvedCodes = [];
// Build approved_codes
// Build approvedCodes
foreach ($this->shipping_options as $shipping_group => $row) {
foreach ($row as $_carrier => $row2) {
if ( ! isset($approved_codes[$_carrier])) {
$approved_codes[$_carrier] = [];
if (!isset($approvedCodes[$_carrier])) {
$approvedCodes[$_carrier] = [];
}
foreach ($row2 as $code => $display) {
$approved_codes[$_carrier][] = $code;
$approvedCodes[$_carrier][] = $code;
}
}
}
if ($carrier !== NULL AND isset($approved_codes[$carrier])) {
return $approved_codes[$carrier];
if ($carrier !== null && isset($approvedCodes[$carrier])) {
return $approvedCodes[$carrier];
}
return $approved_codes;
return $approvedCodes;
}
public function get_display_rates($rates)
public function getDisplayRates($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;
$cheapest_row = null;
foreach ($row as $carrier => $row2) {
$group_codes = array_keys($row2);
if ( ! empty($rates[$carrier])) {
if (! empty($rates[$carrier])) {
foreach ($rates[$carrier] as $row3) {
if (in_array($row3->getCode(), $group_codes)) {
$row3->setCarrier($carrier);
if ($cheapest_row === NULL) {
if ($cheapest_row === null) {
$cheapest_row = $row3;
} else {
if ($row3->getCost() < $cheapest_row->getCost()) {
@ -104,7 +108,7 @@ class Ship
}
// Add row if it exists
if ( ! empty($cheapest_row)) {
if (! empty($cheapest_row)) {
$display_rates[$shipping_group][] = $cheapest_row;
}
}
@ -112,7 +116,7 @@ class Ship
return $display_rates;
}
public function get_all_display_rates($rates)
public function getAllDisplayRates($rates)
{
// Build output array listing all group options
$display_rates = [];
@ -122,7 +126,7 @@ class Ship
foreach ($row as $carrier => $row2) {
$group_codes = array_keys($row2);
if ( ! empty($rates[$carrier])) {
if (!empty($rates[$carrier])) {
foreach ($rates[$carrier] as $row3) {
@ -134,13 +138,13 @@ class Ship
}
}
$this->sort_by_cost($display_rates[$shipping_group]);
$this->sortByCost($display_rates[$shipping_group]);
}
return $display_rates;
}
protected function sort_by_cost( & $rates)
protected function sortByCost(& $rates)
{
uasort($rates, create_function('$a, $b', 'return ($a->getCost() > $b->getCost());'));
}

@ -5,15 +5,16 @@ class Shipment
/** @var Package[] */
protected $packages = [];
protected $from_postal_code;
protected $from_country_code;
protected $to_postal_code;
protected $to_country_code;
/** @var bool */
protected $to_is_residential;
protected $fromIsResidential;
protected $fromPostalCode;
protected $fromCountryCode;
protected $fromStateProvince;
/** @var bool */
protected $from_is_residential;
protected $from_state_province;
protected $toIsResidential;
protected $toPostalCode;
protected $toCountryCode;
/**
* @param Package $package
@ -46,16 +47,16 @@ class Shipment
*/
public function getFromPostalCode()
{
return $this->from_postal_code;
return $this->fromPostalCode;
}
/**
* @param mixed $from_postal_code
* @param mixed $fromPostalCode
* @return $this
*/
public function setFromPostalCode($from_postal_code)
public function setFromPostalCode($fromPostalCode)
{
$this->from_postal_code = $from_postal_code;
$this->fromPostalCode = $fromPostalCode;
return $this;
}
@ -64,16 +65,16 @@ class Shipment
*/
public function getFromCountryCode()
{
return $this->from_country_code;
return $this->fromCountryCode;
}
/**
* @param mixed $from_country_code
* @param mixed $fromCountryCode
* @return $this
*/
public function setFromCountryCode($from_country_code)
public function setFromCountryCode($fromCountryCode)
{
$this->from_country_code = $from_country_code;
$this->fromCountryCode = $fromCountryCode;
return $this;
}
@ -82,16 +83,16 @@ class Shipment
*/
public function getToPostalCode()
{
return $this->to_postal_code;
return $this->toPostalCode;
}
/**
* @param mixed $to_postal_code
* @param mixed $toPostalCode
* @return $this
*/
public function setToPostalCode($to_postal_code)
public function setToPostalCode($toPostalCode)
{
$this->to_postal_code = $to_postal_code;
$this->toPostalCode = $toPostalCode;
return $this;
}
@ -100,52 +101,52 @@ class Shipment
*/
public function getToCountryCode()
{
return $this->to_country_code;
return $this->toCountryCode;
}
/**
* @param mixed $to_country_code
* @param mixed $toCountryCode
* @return $this
*/
public function setToCountryCode($to_country_code)
public function setToCountryCode($toCountryCode)
{
$this->to_country_code = $to_country_code;
$this->toCountryCode = $toCountryCode;
return $this;
}
/**
* @return boolean
*/
public function isToResidential()
public function getToIsResidential()
{
return $this->to_is_residential;
return $this->toIsResidential;
}
/**
* @param boolean $to_is_residential
* @param boolean $toIsResidential
* @return $this
*/
public function setToResidential($to_is_residential)
public function setToIsResidential($toIsResidential)
{
$this->to_is_residential = $to_is_residential;
$this->toIsResidential = $toIsResidential;
return $this;
}
/**
* @return bool
*/
public function isFromResidential()
public function getFromIsResidential()
{
return $this->from_is_residential;
return $this->fromIsResidential;
}
/**
* @param $from_is_residential
* @param $fromIsResidential
* @return $this
*/
public function setFromResidential($from_is_residential)
public function setFromIsResidential($fromIsResidential)
{
$this->from_is_residential = $from_is_residential;
$this->fromIsResidential = $fromIsResidential;
return $this;
}
@ -154,16 +155,16 @@ class Shipment
*/
public function getFromStateProvinceCode()
{
return $this->from_state_province;
return $this->fromStateProvince;
}
/**
* @param $from_state_province
* @param $fromStateProvince
* @return $this
*/
public function setFromStateProvinceCode($from_state_province)
public function setFromStateProvinceCode($fromStateProvince)
{
$this->from_state_province = $from_state_province;
$this->fromStateProvince = $fromStateProvince;
return $this;
}
}

@ -11,20 +11,20 @@ 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 $urlDev = 'https://wwwcie.ups.com/ups.app/xml/Rate';
private $urlProd = 'https://www.ups.com/ups.app/xml/Rate';
private $access_key = 'XXX';
private $user_id = 'XXX';
private $accessKey = 'XXX';
private $userId = 'XXX';
private $password = 'XXX';
private $shipper_number = 'XXX';
private $shipperNumber = 'XXX';
public $approved_codes = [
public $approvedCodes = [
'03',
'12',
];
private $shipping_codes = [
private $shippingCodes = [
'US' => [ // United States
'01' => 'UPS Next Day Air',
'02' => 'UPS 2nd Day Air',
@ -86,118 +86,120 @@ class Rate extends RateAdapter
{
parent::__construct($options);
if (isset($options['access_key'])) {
$this->access_key = $options['access_key'];
}
$this->accessKey = Arr::get($options, 'accessKey');
$this->userId = Arr::get($options, 'userId');
$this->password = Arr::get($options, 'password');
$this->shipperNumber = Arr::get($options, 'shipperNumber');
$this->approvedCodes = Arr::get($options, 'approvedCodes');
if (isset($options['user_id'])) {
$this->user_id = $options['user_id'];
}
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Post()));
if (isset($options['password'])) {
$this->password = $options['password'];
}
if (isset($options['shipper_number'])) {
$this->shipper_number = $options['shipper_number'];
}
protected function prepare()
{
$service_code = '03';
if (isset($options['approved_codes'])) {
$this->approved_codes = $options['approved_codes'];
}
$this->data = '<?xml version="1.0"?>' . "\n" .
'<AccessRequest xml:lang="en-US">' .
'<AccessLicenseNumber>' . $this->accessKey . '</AccessLicenseNumber>'.
'<UserId>' . $this->userId . '</UserId>' .
'<Password>' . $this->password . '</Password>' .
'</AccessRequest>' .
'<RatingServiceSelectionRequest xml:lang="en-US">' .
'<Request>' .
'<RequestAction>Rate</RequestAction>' .
'<RequestOption>shop</RequestOption>' .
'</Request>' .
'<Shipment>' .
'<Shipper>' .
'<Address>' .
'<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>' .
'<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>' .
(
$this->shipment->getFromIsResidential() ?
'<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' :
''
) .
'</Address>' .
'<ShipperNumber>' . $this->shipperNumber . '</ShipperNumber>' .
'</Shipper>' .
'<ShipTo>' .
'<Address>' .
'<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>' .
'<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>' .
(
$this->shipment->getToIsResidential() ?
'<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' :
''
) .
'</Address>' .
'</ShipTo>' .
'<ShipFrom>' .
'<Address>' .
'<StateProvinceCode>' .
$this->shipment->getFromStateProvinceCode() .
'</StateProvinceCode>' .
'<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>' .
'<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>' .
(
$this->shipment->getFromIsResidential() ?
'<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' :
''
) .
'</Address>' .
'</ShipFrom>' .
'<Service>' .
'<Code>' . $service_code . '</Code>' .
'</Service>' .
$this->getPackagesXmlString() .
'<RateInformation>' .
'<NegotiatedRatesIndicator/>' .
'</RateInformation>' .
'</Shipment>' .
'</RatingServiceSelectionRequest>';
if (isset($options['request_adapter'])) {
$this->set_request_adapter($options['request_adapter']);
} else {
$this->set_request_adapter(new RateRequest\Post());
}
return $this;
}
protected function prepare()
private function getPackagesXmlString()
{
$service_code = '03';
$packages = '';
foreach ($this->shipment->getPackages() as $p) {
$packages .= '<Package>
<PackagingType>
<Code>02</Code>
</PackagingType>
<Dimensions>
<UnitOfMeasurement>
<Code>IN</Code>
</UnitOfMeasurement>
<Length>' . $p->getLength() . '</Length>
<Width>' . $p->getWidth() . '</Width>
<Height>' . $p->getHeight() . '</Height>
</Dimensions>
<PackageWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>' . $p->getWeight() . '</Weight>
</PackageWeight>
</Package>';
}
$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>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
<ShipperNumber>' . $this->shipper_number . '</ShipperNumber>
</Shipper>
<ShipTo>
<Address>
<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>
' . (($this->shipment->isToResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
</ShipTo>
<ShipFrom>
<Address>
<StateProvinceCode>' . $this->shipment->getFromStateProvinceCode() . '</StateProvinceCode>
<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
</ShipFrom>
<Service>
<Code>' . $service_code . '</Code>
</Service>
' . $packages . '
<RateInformation>
<NegotiatedRatesIndicator/>
</RateInformation>
</Shipment>
</RatingServiceSelectionRequest>';
return $this;
$packages .=
'<Package>' .
'<PackagingType>' .
'<Code>02</Code>' .
'</PackagingType>' .
'<Dimensions>' .
'<UnitOfMeasurement>' .
'<Code>IN</Code>' .
'</UnitOfMeasurement>' .
'<Length>' . $p->getLength() . '</Length>' .
'<Width>' . $p->getWidth() . '</Width>' .
'<Height>' . $p->getHeight() . '</Height>' .
'</Dimensions>' .
'<PackageWeight>' .
'<UnitOfMeasurement>' .
'<Code>LBS</Code>' .
'</UnitOfMeasurement>' .
'<Weight>' . $p->getWeight() . '</Weight>' .
'</PackageWeight>' .
'</Package>';
}
return $packages;
}
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->url_dev;
$url = $this->urlDev;
}
$this->response = $this->rate_request->execute($url, $this->data);
$this->response = $this->rateRequest->execute($url, $this->data);
return $this;
}
@ -208,14 +210,13 @@ class Rate extends RateAdapter
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
$rate_list = @$dom->getElementsByTagName('RatedShipment');
$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();
echo $this->response;
throw $e;
}
@ -224,13 +225,13 @@ class Rate extends RateAdapter
->getElementsByTagName('Service')->item(0)
->getElementsByTagName('Code')->item(0)->nodeValue;
$name = Arr::get($this->shipping_codes['US'], $code);
$name = Arr::get($this->shippingCodes['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)) {
if (! empty($this->approvedCodes) && ! in_array($code, $this->approvedCodes)) {
continue;
}
@ -239,7 +240,7 @@ class Rate extends RateAdapter
->setCarrier('ups')
->setCode($code)
->setName($name)
->setCost((int) $cost * 100);
->setCost($cost * 100);
$this->rates[] = $quote;
}

@ -11,13 +11,13 @@ 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 $urlDev = 'http://production.shippingapis.com/ShippingAPI.dll';
private $urlProd = 'http://production.shippingapis.com/ShippingAPI.dll';
private $username = 'XXX';
private $password = 'XXX';
public $approved_codes = [
public $approvedCodes = [
'1',
'4',
];
@ -71,32 +71,14 @@ class Rate extends RateAdapter
{
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());
}
$this->username = Arr::get($options, 'username');
$this->password = Arr::get($options, 'password');
$this->approvedCodes = Arr::get($options, 'approvedCodes');
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Get()));
}
protected function prepare()
{
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
@ -104,17 +86,17 @@ class Rate extends RateAdapter
/**
* RateV4Request / Package / Size
required once
Defined as follows:
REGULAR: Package dimensions are 12 or less;
LARGE: Any package dimension is larger than 12.
For example: <Size>REGULAR</Size>
string
whiteSpace=collapse
enumeration=LARGE
enumeration=REGULAR
* required once
* Defined as follows:
*
* REGULAR: Package dimensions are 12 or less;
* LARGE: Any package dimension is larger than 12.
*
* For example: <Size>REGULAR</Size>
* string
* whiteSpace=collapse
* enumeration=LARGE
* enumeration=REGULAR
*/
if ($p->getWidth() > 12 or $p->getLength() > 12 or $p->getHeight() > 12) {
@ -125,41 +107,42 @@ class Rate extends RateAdapter
$container = 'VARIABLE';
}
$packages .= '<Package ID="' . $sequence_number .'">
<Service>ALL</Service>
<ZipOrigination>' . $this->shipment->getFromPostalCode() . '</ZipOrigination>
<ZipDestination>' . $this->shipment->getToPostalCode() . '</ZipDestination>
<Pounds>' . $p->getWeight() . '</Pounds>
<Ounces>0</Ounces>
<Container>' . $container . '</Container>
<Size>' . $size . '</Size>
<Width>' . $p->getWidth() . '</Width>
<Length>' . $p->getLength() . '</Length>
<Height>' . $p->getHeight() . '</Height>
<Machinable>' . 'False' . '</Machinable>
</Package>';
$packages .=
'<Package ID="' . $sequence_number . '">' .
'<Service>ALL</Service>' .
'<ZipOrigination>' . $this->shipment->getFromPostalCode() . '</ZipOrigination>' .
'<ZipDestination>' . $this->shipment->getToPostalCode() . '</ZipDestination>' .
'<Pounds>' . $p->getWeight() . '</Pounds>' .
'<Ounces>0</Ounces>' .
'<Container>' . $container . '</Container>' .
'<Size>' . $size . '</Size>' .
'<Width>' . $p->getWidth() . '</Width>' .
'<Length>' . $p->getLength() . '</Length>' .
'<Height>' . $p->getHeight() . '</Height>' .
'<Machinable>' . 'False' . '</Machinable>' .
'</Package>';
}
$this->data =
'<RateV4Request USERID="' . $this->username . '">
<Revision/>
' . $packages . '
</RateV4Request>';
'<RateV4Request USERID="' . $this->username . '">' .
'<Revision/>' .
$packages .
'</RateV4Request>';
return $this;
}
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->url_dev;
$url = $this->urlDev;
}
$url_request = $url . '?API=RateV4&XML=' . rawurlencode($this->data);
$this->response = $this->rate_request->execute($url_request);
$this->response = $this->rateRequest->execute($url_request);
return $this;
}
@ -176,8 +159,6 @@ class Rate extends RateAdapter
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;
}
@ -190,7 +171,7 @@ class Rate extends RateAdapter
$name = Arr::get($this->shipping_codes['domestic'], $code);
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
if (!empty($this->approvedCodes) && !in_array($code, $this->approvedCodes)) {
continue;
}

@ -0,0 +1,28 @@
<?php
namespace pdt256\Shipping;
class ArrTest extends \PHPUnit_Framework_TestCase
{
public function providerGet()
{
return array(
array(array('uno', 'dos', 'tress'), 1, null, 'dos'),
array(array('we' => 'can', 'make' => 'change'), 'we', null, 'can'),
array(array('uno', 'dos', 'tress'), 10, null, null),
array(array('we' => 'can', 'make' => 'change'), 'he', null, null),
array(array('we' => 'can', 'make' => 'change'), 'he', 'who', 'who'),
array(array('we' => 'can', 'make' => 'change'), 'he', array('arrays'), array('arrays')),
);
}
/**
* @dataProvider providerGet()
*/
public function testGet(array $array, $key, $default, $expected)
{
$this->assertSame(
$expected,
Arr::get($array, $key, $default)
);
}
}

@ -0,0 +1,110 @@
<?php
namespace pdt256\Shipping\Fedex;
use pdt256\Shipping\RateRequest\StubFedex;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Package;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\Quote;
use DateTime;
class RateTest extends \PHPUnit_Framework_TestCase
{
/** @var Shipment */
protected $shipment;
protected $approvedCodes = [];
public function setUp()
{
$ship = Ship::factory([
'Standard Shipping' => [
'fedex' => [
'FEDEX_EXPRESS_SAVER' => '1-3 business days',
'FEDEX_GROUND' => '1-5 business days',
'GROUND_HOME_DELIVERY' => '1-5 business days',
],
],
'Two-Day Shipping' => [
'fedex' => [
'FEDEX_2_DAY' => '2 business days',
],
],
'One-Day Shipping' => [
'fedex' => [
'STANDARD_OVERNIGHT' => 'overnight',
],
],
]);
$this->approvedCodes = $ship->getApprovedCodes('fedex');
$package = new Package;
$package->setWeight(3)
->setWidth(9)
->setLength(9)
->setHeight(9);
$this->shipment = new Shipment;
$this->shipment->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
}
public function testMockRates()
{
$rateAdapter = new Rate([
'prod' => false,
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rates = $rateAdapter->getRates();
$ground = new Quote('fedex', 'GROUND_HOME_DELIVERY', 'Ground Home Delivery', 1655);
$ground->setTransitTime('THREE_DAYS');
$express = new Quote('fedex', 'FEDEX_EXPRESS_SAVER', 'Fedex Express Saver', 2989);
$express->setDeliveryEstimate(new DateTime('2014-09-30T20:00:00'));
$secondDay = new Quote('fedex', 'FEDEX_2_DAY', 'Fedex 2 Day', 4072);
$secondDay->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'));
$overnight = new Quote('fedex', 'STANDARD_OVERNIGHT', 'Standard Overnight', 7834);
$overnight->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'));
$expected = [$ground, $express, $secondDay, $overnight];
$this->assertEquals($expected, $rates);
}
public function testLiveRates()
{
if (getenv('FEDEX_KEY') === false) {
$this->markTestSkipped('Live Fedex credentials missing.');
}
$rateAdapter = new Rate([
'prod' => false,
'key' => getenv('FEDEX_KEY'),
'password' => getenv('FEDEX_PASSWORD'),
'account_number' => getenv('FEDEX_ACCOUNT_NUMBER'),
'meter_number' => getenv('FEDEX_METER_NUMBER'),
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rates = $rateAdapter->getRates();
$this->assertTrue(count($rates) > 0);
$this->assertTrue($rates[0] instanceof Quote);
}
}

@ -0,0 +1,19 @@
<?php
namespace pdt256\Shipping;
class PackageTest extends \PHPUnit_Framework_TestCase
{
public function testCreate()
{
$package = new Package;
$package->setWeight(5);
$package->setWidth(6);
$package->setLength(7);
$package->setHeight(8);
$this->assertEquals(5, $package->getWeight());
$this->assertEquals(6, $package->getWidth());
$this->assertEquals(7, $package->getLength());
$this->assertEquals(8, $package->getHeight());
}
}

@ -0,0 +1,25 @@
<?php
namespace pdt256\Shipping;
use DateTime;
class QuoteTest extends \PHPUnit_Framework_TestCase
{
public function testCreate()
{
$quote = new Quote;
$quote->setCode('-code-');
$quote->setName('Test Name');
$quote->setCost(500);
$quote->setTransitTime('-transit-time-');
$quote->setDeliveryEstimate(new DateTime);
$quote->setCarrier('-carrier-');
$this->assertEquals('-code-', $quote->getCode());
$this->assertEquals('Test Name', $quote->getName());
$this->assertEquals(500, $quote->getCost());
$this->assertEquals('-transit-time-', $quote->getTransitTime());
$this->assertTrue($quote->getDeliveryEstimate() instanceof DateTime);
$this->assertEquals('-carrier-', $quote->getCarrier());
}
}

@ -0,0 +1,13 @@
<?php
namespace pdt256\Shipping;
class RateAdapterTest extends \PHPUnit_Framework_TestCase
{
public function testCreate()
{
/* @var RateAdapter $mock */
$mock = $this->getMockForAbstractClass('pdt256\Shipping\RateAdapter');
$mockRequestAdapter = $this->getMockForAbstractClass('pdt256\Shipping\RateRequest\Adapter');
$mock->setRequestAdapter($mockRequestAdapter);
}
}

@ -1,14 +1,9 @@
<?php
use pdt256\Shipping\Package;
use pdt256\Shipping\Quote;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\USPS;
use pdt256\Shipping\UPS;
use pdt256\Shipping\Fedex;
use pdt256\Shipping\RateRequest;
namespace pdt256\Shipping;
class ShipTest extends PHPUnit_Framework_TestCase
use DateTime;
class ShipTest extends \PHPUnit_Framework_TestCase
{
/** @var Shipment */
public $shipment;
@ -56,7 +51,7 @@ class ShipTest extends PHPUnit_Framework_TestCase
->setFromCountryCode('US')
->setToPostalCode('78703')
->setToCountryCode('US')
->setToResidential(true);
->setToIsResidential(true);
$p = new Package;
$p->setWeight(3)
@ -72,206 +67,89 @@ class ShipTest extends PHPUnit_Framework_TestCase
private function getUSPSOptions()
{
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('usps');
$approvedCodes = $ship->getApprovedCodes('usps');
return [
'prod' => FALSE,
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => $this->shipment,
'approved_codes' => $approved_codes,
'request_adapter' => new RateRequest\StubUSPS(),
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubUSPS(),
];
}
private function getUPSOptions()
{
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('ups');
$approvedCodes = $ship->getApprovedCodes('ups');
return [
'prod' => FALSE,
'access_key' => 'XXXX',
'user_id' => 'XXXX',
'prod' => false,
'accessKey' => 'XXXX',
'userId' => 'XXXX',
'password' => 'XXXX',
'shipper_number' => 'XXXX',
'shipperNumber' => 'XXXX',
'shipment' => $this->shipment,
'approved_codes' => $approved_codes,
'request_adapter' => new RateRequest\StubUPS(),
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubUPS(),
];
}
private function getFedexOptions()
{
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('fedex');
$approvedCodes = $ship->getApprovedCodes('fedex');
return [
'prod' => FALSE,
'prod' => false,
'key' => 'XXXX',
'password' => 'XXXX',
'account_number' => 'XXXX',
'meter_number' => 'XXXX',
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approved_codes' => $approved_codes,
'request_adapter' => new RateRequest\StubFedex(),
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubFedex(),
];
}
public function testUSPSRate()
{
$usps = new USPS\Rate($this->getUSPSOptions());
$usps_rates = $usps->get_rates();
$post = new Quote;
$post
->setCarrier('usps')
->setCode(4)
->setName('Parcel Post')
->setCost(1001);
$priority = new Quote;
$priority
->setCarrier('usps')
->setCode(1)
->setName('Priority Mail')
->setCost(1220);
$expected_return = [$post, $priority];
$this->assertEquals($expected_return, $usps_rates);
}
public function testUPSRate()
{
$ups = new UPS\Rate($this->getUPSOptions());
$ups_rates = $ups->get_rates();
$ground = new Quote;
$ground
->setCarrier('ups')
->setCode('03')
->setName('UPS Ground')
->setCost(1900);
$twodayair = new Quote;
$twodayair
->setCarrier('ups')
->setCode('02')
->setName('UPS 2nd Day Air')
->setCost(4900);
$nextdaysaver = new Quote;
$nextdaysaver
->setCarrier('ups')
->setCode('13')
->setName('UPS Next Day Air Saver')
->setCost(8900);
$nextdayair = new Quote;
$nextdayair
->setCarrier('ups')
->setCode('01')
->setName('UPS Next Day Air')
->setCost(9300);
$expected_return = [$ground, $twodayair, $nextdaysaver, $nextdayair];
$this->assertEquals($expected_return, $ups_rates);
}
public function testFedexRate()
{
$fedex = new Fedex\Rate($this->getFedexOptions());
$fedex_rates = $fedex->get_rates();
$ground = new Quote;
$ground
->setCarrier('fedex')
->setCode('GROUND_HOME_DELIVERY')
->setName('Ground Home Delivery')
->setCost(1600)
->setTransitTime('THREE_DAYS');
$express = new Quote;
$express
->setCarrier('fedex')
->setCode('FEDEX_EXPRESS_SAVER')
->setName('Fedex Express Saver')
->setCost(2900)
->setDeliveryEstimate(new DateTime('2014-09-30T20:00:00'))
->setTransitTime(null);
$secondday = new Quote;
$secondday
->setCarrier('fedex')
->setCode('FEDEX_2_DAY')
->setName('Fedex 2 Day')
->setCost(4000)
->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'))
->setTransitTime(null);
$overnight = new Quote;
$overnight
->setCarrier('fedex')
->setCode('STANDARD_OVERNIGHT')
->setName('Standard Overnight')
->setCost(7800)
->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'))
->setTransitTime(null);
$expected_result = [$ground, $express, $secondday, $overnight];
$this->assertEquals($expected_result, $fedex_rates);
}
public function testDisplayOptions()
{
$rates = [];
$usps = new USPS\Rate($this->getUSPSOptions());
$rates['usps'] = $usps->get_rates();
$rates['usps'] = $usps->getRates();
$ups = new UPS\Rate($this->getUPSOptions());
$rates['ups'] = $ups->get_rates();
$rates['ups'] = $ups->getRates();
$fedex = new Fedex\Rate($this->getFedexOptions());
$rates['fedex'] = $fedex->get_rates();
$rates['fedex'] = $fedex->getRates();
$ship = Ship::factory($this->shipping_options);
$display_rates = $ship->get_display_rates($rates);
$rates = $ship->getDisplayRates($rates);
$post = new Quote;
$post->setCode(4)
->setName('Parcel Post')
->setCost(1001)
->setCarrier('usps');
$post = new Quote('usps', '4', 'Parcel Post', 1001);
$fedex_two_day = new Quote;
$fedex_two_day->setCode('FEDEX_2_DAY')
->setName('Fedex 2 Day')
->setCost(4000)
->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'))
->setCarrier('fedex');
$fedexTwoDay = new Quote('fedex', 'FEDEX_2_DAY', 'Fedex 2 Day', 4072);
$fedexTwoDay->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'));
$overnight = new Quote;
$overnight->setCode('STANDARD_OVERNIGHT')
->setName('Standard Overnight')
->setCost(7800)
->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'))
->setCarrier('fedex');
$overnight = new Quote('fedex', 'STANDARD_OVERNIGHT', 'Standard Overnight', 7834);
$overnight->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'));
$this->assertEquals([
$expected = [
'Standard Shipping' => [
$post,
],
'Two-Day Shipping' => [
$fedex_two_day,
$fedexTwoDay,
],
'One-Day Shipping' => [
$overnight,
],
], $display_rates);
];
$this->assertEquals($expected, $rates);
}
}

@ -0,0 +1,32 @@
<?php
namespace pdt256\Shipping;
class ShipmentTest extends \PHPUnit_Framework_TestCase
{
public function testCreate()
{
$shipment = new Shipment;
$shipment->addPackage(new Package);
$shipment->setFromIsResidential(false);
$shipment->setFromPostalCode('90401');
$shipment->setFromCountryCode('US');
$shipment->setFromStateProvinceCode('CA');
$shipment->setToIsResidential(true);
$shipment->setToPostalCode('90210');
$shipment->setToCountryCode('US');
$this->assertTrue($shipment->getPackages()[0] instanceof Package);
$this->assertEquals(1, $shipment->packageCount());
$this->assertFalse($shipment->getFromIsResidential());
$this->assertEquals('90401', $shipment->getFromPostalCode());
$this->assertEquals('US', $shipment->getFromCountryCode());
$this->assertEquals('CA', $shipment->getFromStateProvinceCode());
$this->assertTrue($shipment->getToIsResidential());
$this->assertEquals('90210', $shipment->getToPostalCode());
$this->assertEquals('US', $shipment->getToCountryCode());
}
}

@ -0,0 +1,99 @@
<?php
namespace pdt256\Shipping\UPS;
use pdt256\Shipping\RateRequest\StubUPS;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Package;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\Quote;
class RateTest extends \PHPUnit_Framework_TestCase
{
/** @var Shipment */
protected $shipment;
protected $approvedCodes = [];
public function setUp()
{
$ship = Ship::factory([
'Standard Shipping' => [
'ups' => [
'03' => '1-5 business days',
],
],
'Two-Day Shipping' => [
'ups' => [
'02' => '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',
],
],
]);
$this->approvedCodes = $ship->getApprovedCodes('ups');
$package = new Package;
$package->setWeight(3)
->setWidth(9)
->setLength(9)
->setHeight(9);
$this->shipment = new Shipment;
$this->shipment->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
}
public function testMockRates()
{
$rateAdapter = new Rate([
'prod' => false,
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUPS,
]);
$rates = $rateAdapter->getRates();
$expected = [
new Quote('ups', '03', 'UPS Ground', 1910),
new Quote('ups', '02', 'UPS 2nd Day Air', 4923),
new Quote('ups', '13', 'UPS Next Day Air Saver', 8954),
new Quote('ups', '01', 'UPS Next Day Air', 9328),
];
$this->assertEquals($expected, $rates);
}
public function testLiveRates()
{
if (getenv('UPS_ACCESS_KEY') === false) {
$this->markTestSkipped('Live UPS credentials missing.');
}
$rateAdapter = new Rate([
'prod' => false,
'accessKey' => getenv('UPS_ACCESS_KEY'),
'userId' => getenv('UPS_USER_ID'),
'password' => getenv('UPS_PASSWORD'),
'shipperNumber' => getenv('UPS_SHIPPER_NUMBER'),
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
]);
$rates = $rateAdapter->getRates();
$this->assertTrue(count($rates) > 0);
$this->assertTrue($rates[0] instanceof Quote);
}
}

@ -0,0 +1,86 @@
<?php
namespace pdt256\Shipping\USPS;
use pdt256\Shipping\RateRequest\StubUSPS;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Package;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\Quote;
class RateTest extends \PHPUnit_Framework_TestCase
{
/** @var Shipment */
protected $shipment;
protected $approvedCodes = [];
public function setUp()
{
$ship = Ship::factory([
'Standard Shipping' => [
'usps' => [
'1' => '1-3 business days',
'4' => '2-8 business days',
],
],
]);
$this->approvedCodes = $ship->getApprovedCodes('usps');
$package = new Package;
$package->setWeight(3)
->setWidth(9)
->setLength(9)
->setHeight(9);
$this->shipment = new Shipment;
$this->shipment->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
}
public function testMockRates()
{
$rateAdapter = new Rate([
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUSPS,
]);
$rates = $rateAdapter->getRates();
$expected = [
new Quote('usps', '4', 'Parcel Post', 1001),
new Quote('usps', '1', 'Priority Mail', 1220),
];
$this->assertEquals($expected, $rates);
}
public function testLiveRates()
{
if (getenv('USPS_USERNAME') === false) {
$this->markTestSkipped('Live USPS credentials missing.');
}
$rateAdapter = new Rate([
'prod' => false,
'username' => getenv('USPS_USERNAME'),
'password' => getenv('USPS_PASSWORD'),
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
]);
$rates = $rateAdapter->getRates();
$this->assertTrue(count($rates) > 0);
$this->assertTrue($rates[0] instanceof Quote);
}
}
Loading…
Cancel
Save