Compare commits

..

50 Commits

Author SHA1 Message Date
b04134946d Fix typo 2020-05-29 17:40:13 -06:00
ef5bbc91dd Add pounds/ounces 2020-05-29 17:38:34 -06:00
Hari KT
9a9fca51d5 update timeout 2019-12-12 09:42:56 -08:00
Jamie Isaacs
4139f52d14 Fixed phpunit coverage whitelist 2019-12-12 01:09:03 -08:00
Jamie Isaacs
8a4a7ebbdf Fixed composer to work with packagist. Fixed risky tests. 2019-12-12 00:29:28 -08:00
Jamie Isaacs
8d1cd60d1a
Merge pull request #19 from harikt/create-func
create_function has been DEPRECATED as of PHP 7.2.0
2019-12-11 23:55:52 -08:00
Hari K T
2e920b5449
Merge branch 'master' into create-func 2019-12-12 12:21:50 +05:30
Hari KT
0e115da493 upgrade phpunit and codesniffer 2019-12-12 12:20:22 +05:30
Jamie Isaacs
2a5050632a Upgraded to PHP 7.1 for a 1.0.0 release 2019-12-11 14:55:57 -08:00
Jamie Isaacs
a11a122c7e Upgraded to PHP 7.1 for a 1.0.0 release 2019-12-11 14:55:29 -08:00
Hari KT
7a89fea6f6 update travis 2019-12-11 12:57:25 +05:30
Hari KT
0a494a591f create_function has been DEPRECATED as of PHP 7.2.0 2019-12-11 12:50:54 +05:30
Jamie Isaacs
190b1d74c9 Changing private method to protected per #15 2017-08-14 10:05:45 -07:00
Dranzd Viper
0aa709e251 Fix issue #12, Call curl_close after curl_error 2017-07-31 21:03:28 -07:00
Jamie Isaacs
d1281805ea Commenting out TLSv1 from cipher list that was causing problems. Updating readme. resolves #8. 2014-12-11 02:31:22 -08:00
Jamie Isaacs
6233359c57 Fixing for PSR-2. 2014-12-08 22:55:53 -08:00
Jamie Isaacs
71a7383b73 Merge pull request #10 from perk11/validation
Added tests for Validation
2014-12-08 22:53:11 -08:00
Pereyaslov Konstantin
053184f493 Tests for missing package dimensions 2014-12-09 09:32:46 +03:00
Pereyaslov Konstantin
ae4552b0e0 Moved packages validation to methodi validatePackages() in RateAdapter to avoid duplication of code
.Added width validation.
2014-12-09 09:32:20 +03:00
Pereyaslov Konstantin
49321237df Added common setters and getters to RateAdapter for:
-shipment
-isProduction
2014-12-09 09:12:40 +03:00
Pereyaslov Konstantin
dfd9f50c8f Tests for missing data 2014-12-08 22:38:07 +03:00
Pereyaslov Konstantin
535db69152 Validator test 2014-12-08 22:36:06 +03:00
Jamie Isaacs
51a8b8e331 Merge pull request #9 from perk11/validation
Validation
2014-12-08 10:51:36 -08:00
Pereyaslov Konstantin
04cae3c295 Fixed formatting to match PSR-2 2014-12-08 20:15:36 +03:00
Pereyaslov Konstantin
2938be5a2d Moving default values to __construct, because otherwise they get overwritten 2014-12-08 19:34:31 +03:00
Pereyaslov Konstantin
6d6853cbda Setting all options in tests 2014-12-08 19:29:23 +03:00
Pereyaslov Konstantin
10f8bde7a2 Added validation that all necessary properties are set 2014-12-08 19:10:31 +03:00
Pereyaslov Konstantin
5dafdf17a0 broken use removed 2014-12-08 18:56:58 +03:00
Pereyaslov Konstantin
317155b8c2 Merge https://github.com/pdt256/shipping 2014-12-08 18:02:13 +03:00
Jamie Isaacs
3fb378c35e Fixing phpstorm project files. 2014-12-08 02:13:59 -08:00
Jamie Isaacs
eb47cbd422 Update README.md 2014-12-08 01:46:09 -08:00
Jamie Isaacs
ccf447f755 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. 2014-12-08 01:44:21 -08:00
Jamie Isaacs
01ddc321ce Merge pull request #6 from perk11/exceptions
Thanks Konstantin!
2014-12-07 21:00:53 -08:00
Pereyaslov Konstantin
f04c23cb0c Merge https://github.com/pdt256/shipping
Conflicts:
	src/USPS/Rate.php
2014-12-08 07:57:04 +03:00
Pereyaslov Konstantin
1b763a3656 Merge https://github.com/pdt256/shipping into exceptions 2014-12-08 07:53:56 +03:00
Pereyaslov Konstantin
fe2009a7fb Added RequestException. It is thrown when request fails. 2014-12-08 07:23:21 +03:00
Jamie Isaacs
0fdd82dd76 Merge pull request #3 from troydavisson/master
Looks good. Thanks!
2014-12-07 16:08:34 -08:00
Pereyaslov Konstantin
bfc91cd730 Determine shipment size automatically for USPS 2014-12-07 13:03:42 +03:00
Pereyaslov Konstantin
6a62d17a59 Duplicate code removed 2014-12-07 12:21:53 +03:00
Troy Davisson
66d7f46d61 Drop Carbon requirement 2014-11-30 21:51:16 -05:00
Troy Davisson
b911e8923d Merge branch 'master' of github.com:pdt256/shipping
Conflicts:
	src/Fedex/Rate.php
	src/UPS/Rate.php
	src/USPS/Rate.php
	tests/ShipTest.php
2014-11-30 21:42:33 -05:00
Troy Davisson
3dbfc3e135 Specify the container based on the package size 2014-11-28 22:19:02 -05:00
Troy Davisson
c05c14bcc1 Change to the carbon dependency 2014-11-28 12:08:15 -05:00
Troy Davisson
64dbeee35d Updates to the documentation example return values 2014-11-27 13:33:40 -05:00
Troy Davisson
1f98232ac0 Provide rate quotes back as objects 2014-11-27 13:27:56 -05:00
Troy Davisson
6d42cd1273 Readme update 2014-11-27 12:38:22 -05:00
Troy Davisson
14c35aa03f Streamlined the packaging stuff with some reasonable assumed defaults for now 2014-11-27 12:37:33 -05:00
Troy Davisson
ad3c950d6d Update read me 2014-11-27 12:18:41 -05:00
Troy Davisson
a89504e722 Expanded to support multiple packages per request, and now more object oriented.
USPS still provides the cost of each package back individually, so we sum those up per service requested
2014-11-27 01:44:07 -05:00
Troy Davisson
9d7c8335ff Request negotiated rates from UPS 2014-11-27 00:30:48 -05:00
40 changed files with 6412 additions and 1352 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
vendor
composer.lock
coverage_report
.idea/*
!.idea/runConfigurations/
.DS_Store
live_phpunit.sh
/nbproject/private/

View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Unit Tests" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
<TestRunner class="pdt256\Shipping\PackageDimensionsValidationTrait" configuration_file="$PROJECT_DIR$/phpunit.xml" directory="$PROJECT_DIR$" file="$PROJECT_DIR$/tests/PackageDimensionsValidationTrait.php" scope="XML" use_alternative_configuration_file="true" />
<method v="2" />
</configuration>
</component>

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
before_script:
- composer install --prefer-dist
script:
- vendor/bin/phpcs --standard=PSR2 src/ tests/
- vendor/bin/phpunit

View File

@ -1,6 +1,7 @@
The MIT License (MIT)
Copyright (c) 2014 Jamie Isaacs <pdt256@gmail.com>
Copyright (c) 2020 Netsyms Technologies <opensource@netsyms.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

316
README.md
View File

@ -1,54 +1,55 @@
## 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-86%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.
## Installation
## Introduction
Add the following lines to your ``composer.json`` file.
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.
```JSON
{
"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,
],
];
$shipment = new Shipment;
$shipment
->setFromIsResidential(false)
->setFromStateProvinceCode('IN')
->setFromPostalCode('46205')
->setFromCountryCode('US')
->setToIsResidential(true)
->setToPostalCode('20101')
->setToCountryCode('US');
$package = new Package;
$package
->setLength(12)
->setWidth(4)
->setHeight(3)
->setWeight(3);
$shipment->addPackage($package);
```
## UPS (Stub) Example
Below is an example request to get shipping rates from the UPS API.
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.
```php
'request_adapter' => new RateRequest\StubUPS(),
'requestAdapter' => new RateRequest\StubUPS(),
```
```php
@ -56,53 +57,65 @@ 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(),
'prod' => false,
'accessKey' => 'XXXX',
'userId' => 'XXXX',
'password' => 'XXXX',
'shipperNumber' => 'XXXX',
'shipment' => $shipment,
'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
],
'requestAdapter' => new RateRequest\StubUPS(),
]);
$ups_rates = $ups->get_rates();
$rates = $ups->getRates();
```
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,
),
0 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '03',
'name' => 'UPS Ground',
'cost' => 1910,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'ups',
)),
1 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '02',
'name' => 'UPS 2nd Day Air',
'cost' => 4923,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'ups',
)),
2 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '13',
'name' => 'UPS Next Day Air Saver',
'cost' => 8954,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'ups',
)),
3 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '01',
'name' => 'UPS Next Day Air',
'cost' => 9328,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'ups',
)),
)
```
@ -113,39 +126,42 @@ use pdt256\Shipping\USPS;
use pdt256\Shipping\RateRequest;
$usps = new USPS\Rate([
'prod' => FALSE,
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => array_merge($shipment, [
'size' => 'LARGE',
'container' => 'RECTANGULAR',
]),
'approved_codes' => [
'shipment' => $shipment,
'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();
$rates = $usps->getRates();
```
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,
),
0 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1001,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'usps',
)),
1 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => '1',
'name' => 'Priority Mail',
'cost' => 1220,
'transitTime' => NULL,
'deliveryEstimate' => NULL,
'carrier' => 'usps',
)),
)
```
@ -159,64 +175,106 @@ $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' => [
'accountNumber' => 'XXXX',
'meterNumber' => 'XXXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $shipment,
'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();
$rates = $fedex->getRates();
```
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,
),
0 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => 'GROUND_HOME_DELIVERY',
'name' => 'Ground Home Delivery',
'cost' => 1655,
'transitTime' => 'THREE_DAYS',
'deliveryEstimate' => NULL,
'carrier' => 'fedex',
)),
1 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => 'FEDEX_EXPRESS_SAVER',
'name' => 'Fedex Express Saver',
'cost' => 2989,
'transitTime' => NULL,
'deliveryEstimate' =>
DateTime::__set_state(array(
'date' => '2014-09-30 20:00:00',
'timezone_type' => 3,
'timezone' => 'UTC',
)),
'carrier' => 'fedex',
)),
2 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4072,
'transitTime' => NULL,
'deliveryEstimate' =>
DateTime::__set_state(array(
'date' => '2014-09-29 20:00:00',
'timezone_type' => 3,
'timezone' => 'UTC',
)),
'carrier' => 'fedex',
)),
3 =>
pdt256\Shipping\Quote::__set_state(array(
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7834,
'transitTime' => NULL,
'deliveryEstimate' =>
DateTime::__set_state(array(
'date' => '2014-09-26 20:00:00',
'timezone_type' => 3,
'timezone' => 'UTC',
)),
'carrier' => 'fedex',
)),
)
```
## 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)

View File

@ -1,5 +1,5 @@
{
"name": "pdt256/shipping",
"name": "netsyms/shippingrates",
"description": "Shipping Rate API",
"license": "MIT",
"keywords": ["ship", "ups", "usps", "fedex"],
@ -7,11 +7,23 @@
{
"name": "Jamie Isaacs",
"email": "pdt256@gmail.com"
},
{
"name": "Netsyms Technologies",
"email": "opensource@netsyms.com"
}
],
"require": {},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"require-dev": {
"phpunit/phpunit": "4.0.*"
"phpunit/phpunit": "7.0.*",
"squizlabs/php_codesniffer": "3.5.*"
},
"require": {
"php": ">=7.1"
},
"autoload": {
"psr-4": {

16
live_phpunit.sh.example Executable file
View File

@ -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

View File

@ -0,0 +1,22 @@
auxiliary.org-netbeans-modules-php-phpunit.bootstrap_2e_create_2e_tests=false
auxiliary.org-netbeans-modules-php-phpunit.bootstrap_2e_enabled=false
auxiliary.org-netbeans-modules-php-phpunit.bootstrap_2e_path=
auxiliary.org-netbeans-modules-php-phpunit.configuration_2e_enabled=false
auxiliary.org-netbeans-modules-php-phpunit.configuration_2e_path=
auxiliary.org-netbeans-modules-php-phpunit.customSuite_2e_enabled=false
auxiliary.org-netbeans-modules-php-phpunit.customSuite_2e_path=
auxiliary.org-netbeans-modules-php-phpunit.phpUnit_2e_enabled=false
auxiliary.org-netbeans-modules-php-phpunit.phpUnit_2e_path=
auxiliary.org-netbeans-modules-php-phpunit.test_2e_groups_2e_ask=false
auxiliary.org-netbeans-modules-php-phpunit.test_2e_run_2e_all=false
auxiliary.org-netbeans-modules-php-phpunit.test_2e_run_2e_phpunit_2e_only=false
file.reference.ShippingRates-tests=tests
include.path=${php.global.include.path}
php.version=PHP_73
source.encoding=UTF-8
src.dir=src
tags.asp=false
tags.short=false
test.src.dir=${file.reference.ShippingRates-tests}
testing.providers=PhpUnit
web.root=.

9
nbproject/project.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.php.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>ShippingRates</name>
</data>
</configuration>
</project>

View File

@ -1,17 +1,22 @@
<?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>
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>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit>

View File

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

View File

@ -1,236 +1,249 @@
<?php
namespace pdt256\Shipping\Fedex;
use DateTime;
use pdt256\Shipping;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use pdt256\Shipping\Validator;
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 $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 $key;
private $password;
private $accountNumber;
private $meterNumber;
/**
* Type of Drop off, default value "BUSINESS_SERVICE_CENTER" is defined in __construct if not specified.
*/
private $dropOffType;
/**
* Codes of appropriate shipping types. Default value is specified in __construct.
*/
public $approvedCodes;
public $approved_codes = [
'PRIORITY_OVERNIGHT',
'FEDEX_2_DAY',
'FEDEX_EXPRESS_SAVER',
'FEDEX_GROUND',
'GROUND_HOME_DELIVERY',
];
private $shippingCodes = [
'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',
];
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);
public function __construct($options = [])
{
parent::__construct($options);
$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', [
'PRIORITY_OVERNIGHT',
'FEDEX_2_DAY',
'FEDEX_EXPRESS_SAVER',
'FEDEX_GROUND',
'GROUND_HOME_DELIVERY',
]);
$this->dropOffType = Arr::get($options, 'dropOffType', 'BUSINESS_SERVICE_CENTER');
if (isset($options['key'])) {
$this->key = $options['key'];
}
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Post()));
}
protected function validate()
{
$this->validatePackages();
Validator::checkIfNull($this->key, 'key');
Validator::checkIfNull($this->password, 'password');
Validator::checkIfNull($this->accountNumber, 'accountNumber');
Validator::checkIfNull($this->meterNumber, 'meterNumber');
Validator::checkIfNull($this->shipment->getFromPostalCode(), 'fromPostalCode');
Validator::checkIfNull($this->shipment->getFromCountryCode(), 'fromCountryCode');
Validator::checkIfNull($this->shipment->getFromIsResidential(), 'fromIsResidential');
Validator::checkIfNull($this->shipment->getToPostalCode(), 'toPostalCode');
Validator::checkIfNull($this->shipment->getToCountryCode(), 'toCountryCode');
Validator::checkIfNull($this->shipment->getToIsResidential(), 'toIsResidential');
if (isset($options['password'])) {
$this->password = $options['password'];
}
return $this;
}
protected function prepare()
{
$date = time();
$day_name = date('l', $date);
if (isset($options['account_number'])) {
$this->account_number = $options['account_number'];
}
if ($day_name == 'Saturday') {
$date += 172800;
} elseif ($day_name == 'Sunday') {
$date += 86400;
}
if (isset($options['meter_number'])) {
$this->meter_number = $options['meter_number'];
}
// 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
if (isset($options['approved_codes'])) {
$this->approved_codes = $options['approved_codes'];
}
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $package) {
$sequence_number++;
if (isset($options['drop_off_type'])) {
$this->drop_off_type = $options['drop_off_type'];
}
$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>';
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()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
protected function execute()
{
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->urlDev;
}
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
$this->response = $this->rateRequest->execute($url, $this->data);
if ($pounds < 1) {
throw new Exception('Weight missing');
}
return $this;
}
$date = time();
$day_name = date('l', $date);
protected function process()
{
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
$rate_reply = $dom->getElementsByTagName('RateReplyDetails');
if ($day_name == 'Saturday') {
$date += 172800;
} elseif ($day_name == 'Sunday') {
$date += 86400;
}
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;
}
// 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
foreach ($rate_reply as $rate) {
$code = $rate->getElementsByTagName('ServiceType')->item(0)->nodeValue;
$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>';
if (! empty($this->approvedCodes) && ! in_array($code, $this->approvedCodes)) {
continue;
}
return $this;
}
$name = Arr::get($this->shippingCodes, $code);
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
} else {
$url = $this->url_dev;
}
$delivery_ts = @$rate->getElementsByTagName('DeliveryTimestamp')->item(0)->nodeValue;
$transit_time = @$rate->getElementsByTagName('TransitTime')->item(0)->nodeValue;
$this->response = $this->rate_request->execute($url, $this->data);
$cost = $rate
->getElementsByTagName('RatedShipmentDetails')->item(0)
->getElementsByTagName('ShipmentRateDetail')->item(0)
->getElementsByTagName('TotalNetCharge')->item(0)
->getElementsByTagName('Amount')->item(0)->nodeValue;
return $this;
}
$quote = new Quote;
$quote
->setCarrier('fedex')
->setCode($code)
->setName($name)
->setCost((int) ($cost * 100))
->setTransitTime($transit_time);
if ($delivery_ts) {
$quote->setDeliveryEstimate(new DateTime($delivery_ts));
}
protected function process()
{
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
$rate_reply = $dom->getElementsByTagName('RateReplyDetails');
$this->rates[] = $quote;
}
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;
}
return $this;
}
}

105
src/Package.php Normal file
View File

@ -0,0 +1,105 @@
<?php
namespace pdt256\Shipping;
class Package {
protected $pounds;
protected $ounces;
protected $width;
protected $length;
protected $height;
public function getWeight() {
$weight = 0;
if (!empty($this->getPounds())) {
$weight += $this->getPounds();
}
if (!empty($this->getOunces())) {
$weight += ($this->getOunces() / 16);
}
if ($weight == 0) {
return null;
}
return $weight;
}
public function getPounds() {
return $this->pounds ?? 0;
}
public function getOunces() {
return $this->ounces ?? 0;
}
public function setWeight($pounds) {
return $this->setPounds($pounds)->setOunces(0);
}
/**
* @param mixed $pounds
* @return $this
*/
public function setPounds($pounds) {
$this->pounds = $pounds;
return $this;
}
/**
* @param mixed $ounces
* @return $this
*/
public function setOunces($ounces) {
$this->ounces = $ounces;
return $this;
}
/**
* @return mixed
*/
public function getWidth() {
return $this->width;
}
/**
* @param mixed $width
* @return $this
*/
public function setWidth($width) {
$this->width = $width;
return $this;
}
/**
* @return mixed
*/
public function getLength() {
return $this->length;
}
/**
* @param mixed $length
* @return $this
*/
public function setLength($length) {
$this->length = $length;
return $this;
}
/**
* @return mixed
*/
public function getHeight() {
return $this->height;
}
/**
* @param mixed $height
* @return $this
*/
public function setHeight($height) {
$this->height = $height;
return $this;
}
}

131
src/Quote.php Normal file
View File

@ -0,0 +1,131 @@
<?php namespace pdt256\Shipping;
use DateTime;
class Quote
{
protected $code;
protected $name;
protected $cost;
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
*/
public function getCarrier()
{
return $this->carrier;
}
/**
* @param mixed $carrier
* @return $this
*/
public function setCarrier($carrier)
{
$this->carrier = (string) $carrier;
return $this;
}
/**
* @return mixed
*/
public function getCode()
{
return $this->code;
}
/**
* @param string $code
* @return $this
*/
public function setCode($code)
{
$this->code = (string) $code;
return $this;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return $this
*/
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
/**
* @return mixed
*/
public function getCost()
{
return $this->cost;
}
/**
* Quoted cost of this service, in pennies
*
* @param int $cost
* @return $this
*/
public function setCost($cost)
{
$this->cost = (int) $cost;
return $this;
}
/**
* @return mixed
*/
public function getTransitTime()
{
return $this->transitTime;
}
/**
* @param mixed $transitTime
* @return $this
*/
public function setTransitTime($transitTime)
{
$this->transitTime = $transitTime;
return $this;
}
/**
* @return mixed
*/
public function getDeliveryEstimate()
{
return $this->deliveryEstimate;
}
/**
* @param DateTime $deliveryEstimate
* @return $this
*/
public function setDeliveryEstimate(DateTime $deliveryEstimate)
{
$this->deliveryEstimate = $deliveryEstimate;
return $this;
}
}

View File

@ -1,64 +1,108 @@
<?php
namespace pdt256\Shipping;
use Exception;
abstract class RateAdapter
{
protected $is_prod = FALSE;
protected $isProduction;
protected $shipment;
protected $data;
protected $response;
protected $rates = [];
/** @var Shipment */
protected $shipment;
protected $data;
protected $response;
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
/**
* Make sure all necessary fields are set
* @return self
*/
abstract protected function validate();
public function __construct($options = [])
{
if (isset($options['prod'])) {
$this->is_prod = (bool) $options['prod'];
}
/**
* Prepare XML
* @return self
*/
abstract protected function prepare();
if (isset($options['shipment'])) {
$this->shipment = $options['shipment'];
}
/**
* Curl Request
* @return self
*/
abstract protected function execute();
if (empty($this->shipment['to'])) {
throw new Exception('Shipment "to" missing');
}
/**
* Convert to shipping rates array
* @return self
*/
abstract protected function process();
if (empty($this->shipment['from'])) {
throw new Exception('Shipment "from" missing');
}
/**
* @throws \LogicException
* To be called from validate() when packages have to have 3 dimensions and weight
*/
protected function validatePackages()
{
foreach ($this->shipment->getPackages() as $package) {
Validator::checkIfNull($package->getWeight(), 'weight');
Validator::checkIfNull($package->getLength(), 'length');
Validator::checkIfNull($package->getHeight(), 'height');
Validator::checkIfNull($package->getWidth(), 'width');
}
}
public function __construct($options = [])
{
$this->rates = [];
if (empty($this->shipment['dimensions'])) {
throw new Exception('Shipment "dimensions" missing');
}
}
$this->isProduction = (bool) Arr::get($options, 'prod', false);
$this->shipment = Arr::get($options, 'shipment');
}
public function set_request_adapter(RateRequest\Adapter $rate_request)
{
$this->rate_request = $rate_request;
}
public function setRequestAdapter(RateRequest\Adapter $rateRequest)
{
$this->rateRequest = $rateRequest;
}
public function get_rates()
{
$this
->prepare()
->execute()
->process()
->sort_by_cost();
public function setShipment($shipment)
{
$this->shipment = $shipment;
}
return $this->rates;
}
public function getShipment()
{
return $this->shipment;
}
protected function sort_by_cost()
{
uasort($this->rates, create_function('$a, $b', 'return ($a["cost"] > $b["cost"]);'));
}
public function setIsProduction($isProduction): void
{
$this->isProduction = $isProduction;
}
public function getIsProduction(): bool
{
return $this->isProduction;
}
/**
* @return Quote[]
*/
public function getRates(): array
{
$this
->validate()
->prepare()
->execute()
->process()
->sortByCost();
return array_values($this->rates);
}
protected function sortByCost(): void
{
uasort($this->rates, static function (Quote $a, Quote $b) {
return ($a->getCost() > $b->getCost());
});
}
}

View File

@ -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 = 1500;
protected $curlDownloadTimeoutInSeconds = 50;
abstract public function execute($url, $data = NULL);
abstract public function execute($url, $data = null);
}

File diff suppressed because it is too large Load Diff

View File

@ -3,20 +3,25 @@ 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;
}
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_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) {
$error = curl_error($ch);
curl_close($ch);
throw new RequestException($error);
}
curl_close($ch);
return $response;
}
}

View File

@ -3,21 +3,25 @@ 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);
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_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));
}
curl_close($ch);
return $response;
}
return $response;
}
}

View File

@ -0,0 +1,6 @@
<?php
namespace pdt256\Shipping\RateRequest;
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

View File

@ -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;
}
public function __construct($artificial_delay = 0)
{
$this->artificialDelay = $artificial_delay;
}
public function execute($url, $data = NULL)
{
if ($this->artificial_delay > 0) {
sleep($this->artificial_delay);
}
public function execute($url, $data = null)
{
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');
}
}

View File

@ -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>

View File

@ -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>

View File

@ -3,145 +3,146 @@ 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',
],
],
];
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();
public static function factory($shipping_options = [])
{
$object = new self();
if ( ! empty($shipping_options)) {
$object->shipping_options = $shipping_options;
}
if (!empty($shipping_options)) {
$object->shipping_options = $shipping_options;
}
return $object;
}
return $object;
}
public function get_approved_codes($carrier = NULL) {
$approved_codes = [];
/**
* @return array
*/
public function getApprovedCodes($carrier = null)
{
$approvedCodes = [];
// Build approved_codes
foreach ($this->shipping_options as $shipping_group => $row) {
// Build approvedCodes
foreach ($this->shipping_options as $shipping_group => $row) {
foreach ($row as $_carrier => $row2) {
if (!isset($approvedCodes[$_carrier])) {
$approvedCodes[$_carrier] = [];
}
foreach ($row as $_carrier => $row2) {
if ( ! isset($approved_codes[$_carrier])) {
$approved_codes[$_carrier] = [];
}
foreach ($row2 as $code => $display) {
$approvedCodes[$_carrier][] = $code;
}
}
}
foreach ($row2 as $code => $display) {
$approved_codes[$_carrier][] = $code;
}
}
}
if ($carrier !== null && isset($approvedCodes[$carrier])) {
return $approvedCodes[$carrier];
}
if ($carrier !== NULL AND isset($approved_codes[$carrier])) {
return $approved_codes[$carrier];
}
return $approvedCodes;
}
return $approved_codes;
}
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;
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);
foreach ($row as $carrier => $row2) {
$group_codes = array_keys($row2);
if (! empty($rates[$carrier])) {
foreach ($rates[$carrier] as $row3) {
if (in_array($row3->getCode(), $group_codes)) {
$row3->setCarrier($carrier);
if ( ! empty($rates[$carrier])) {
if ($cheapest_row === null) {
$cheapest_row = $row3;
} else {
if ($row3->getCost() < $cheapest_row->getCost()) {
$cheapest_row = $row3;
}
}
}
}
}
}
foreach ($rates[$carrier] as $row3) {
// Add row if it exists
if (! empty($cheapest_row)) {
$display_rates[$shipping_group][] = $cheapest_row;
}
}
if (in_array($row3['code'], $group_codes)) {
$row3['carrier'] = $carrier;
return $display_rates;
}
if ($cheapest_row === NULL) {
$cheapest_row = $row3;
} else {
if ($row3['cost'] < $cheapest_row['cost']) {
$cheapest_row = $row3;
}
}
}
}
}
}
public function getAllDisplayRates($rates)
{
// Build output array listing all group options
$display_rates = [];
foreach ($this->shipping_options as $shipping_group => $row) {
$display_rates[$shipping_group] = [];
// Add row if it exists
if ( ! empty($cheapest_row)) {
$display_rates[$shipping_group][] = $cheapest_row;
}
}
foreach ($row as $carrier => $row2) {
$group_codes = array_keys($row2);
return $display_rates;
}
if (!empty($rates[$carrier])) {
foreach ($rates[$carrier] as $row3) {
if (in_array($row3->getCode(), $group_codes)) {
$row3->setCarrier($carrier);
$display_rates[$shipping_group][] = $row3;
}
}
}
}
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] = [];
$this->sortByCost($display_rates[$shipping_group]);
}
foreach ($row as $carrier => $row2) {
$group_codes = array_keys($row2);
return $display_rates;
}
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"]);'));
}
protected function sortByCost(&$rates)
{
uasort($rates, function ($a, $b) {
return ($a->getCost() > $b->getCost());
});
}
}

170
src/Shipment.php Normal file
View File

@ -0,0 +1,170 @@
<?php namespace pdt256\Shipping;
class Shipment
{
/** @var Package[] */
protected $packages = [];
/** @var bool */
protected $fromIsResidential;
protected $fromPostalCode;
protected $fromCountryCode;
protected $fromStateProvince;
/** @var bool */
protected $toIsResidential;
protected $toPostalCode;
protected $toCountryCode;
/**
* @param Package $package
* @return $this
*/
public function addPackage(Package $package)
{
$this->packages[] = $package;
return $this;
}
/**
* @return Package[]
*/
public function getPackages()
{
return $this->packages;
}
/**
* @return int
*/
public function packageCount()
{
return count($this->getPackages());
}
/**
* @return mixed
*/
public function getFromPostalCode()
{
return $this->fromPostalCode;
}
/**
* @param mixed $fromPostalCode
* @return $this
*/
public function setFromPostalCode($fromPostalCode)
{
$this->fromPostalCode = $fromPostalCode;
return $this;
}
/**
* @return mixed
*/
public function getFromCountryCode()
{
return $this->fromCountryCode;
}
/**
* @param mixed $fromCountryCode
* @return $this
*/
public function setFromCountryCode($fromCountryCode)
{
$this->fromCountryCode = $fromCountryCode;
return $this;
}
/**
* @return mixed
*/
public function getToPostalCode()
{
return $this->toPostalCode;
}
/**
* @param mixed $toPostalCode
* @return $this
*/
public function setToPostalCode($toPostalCode)
{
$this->toPostalCode = $toPostalCode;
return $this;
}
/**
* @return mixed
*/
public function getToCountryCode()
{
return $this->toCountryCode;
}
/**
* @param mixed $toCountryCode
* @return $this
*/
public function setToCountryCode($toCountryCode)
{
$this->toCountryCode = $toCountryCode;
return $this;
}
/**
* @return boolean
*/
public function getToIsResidential()
{
return $this->toIsResidential;
}
/**
* @param boolean $toIsResidential
* @return $this
*/
public function setToIsResidential($toIsResidential)
{
$this->toIsResidential = $toIsResidential;
return $this;
}
/**
* @return bool
*/
public function getFromIsResidential()
{
return $this->fromIsResidential;
}
/**
* @param $fromIsResidential
* @return $this
*/
public function setFromIsResidential($fromIsResidential)
{
$this->fromIsResidential = $fromIsResidential;
return $this;
}
/**
* @return mixed
*/
public function getFromStateProvinceCode()
{
return $this->fromStateProvince;
}
/**
* @param $fromStateProvince
* @return $this
*/
public function setFromStateProvinceCode($fromStateProvince)
{
$this->fromStateProvince = $fromStateProvince;
return $this;
}
}

View File

@ -1,247 +1,266 @@
<?php
namespace pdt256\Shipping\UPS;
use pdt256\Ship;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use pdt256\Shipping\Validator;
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 $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 $password = 'XXX';
private $shipper_number = 'XXX';
private $accessKey;
private $userId;
private $password;
private $shipperNumber;
/**
* Codes of appropriate shipping types. Default value is specified in __construct.
*/
public $approvedCodes;
public $approved_codes = [
'03',
'12',
];
private $shippingCodes = [
'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',
],
];
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);
public function __construct($options = [])
{
parent::__construct($options);
$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', [
'03',
'12',
]);
if (isset($options['access_key'])) {
$this->access_key = $options['access_key'];
}
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Post()));
}
if (isset($options['user_id'])) {
$this->user_id = $options['user_id'];
}
protected function validate()
{
$this->validatePackages();
Validator::checkIfNull($this->accessKey, 'accessKey');
Validator::checkIfNull($this->userId, 'userId');
Validator::checkIfNull($this->password, 'password');
Validator::checkIfNull($this->shipperNumber, 'shipperNumber');
Validator::checkIfNull($this->shipment->getFromPostalCode(), 'fromPostalCode');
Validator::checkIfNull($this->shipment->getFromCountryCode(), 'fromCountryCode');
Validator::checkIfNull($this->shipment->getFromIsResidential(), 'fromIsResidential');
Validator::checkIfNull($this->shipment->getToPostalCode(), 'toPostalCode');
Validator::checkIfNull($this->shipment->getToCountryCode(), 'toCountryCode');
Validator::checkIfNull($this->shipment->getToIsResidential(), 'toIsResidential');
if (isset($options['password'])) {
$this->password = $options['password'];
}
return $this;
}
protected function prepare()
{
$service_code = '03';
if (isset($options['shipper_number'])) {
$this->shipper_number = $options['shipper_number'];
}
$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['approved_codes'])) {
$this->approved_codes = $options['approved_codes'];
}
return $this;
}
if (isset($options['request_adapter'])) {
$this->set_request_adapter($options['request_adapter']);
} else {
$this->set_request_adapter(new RateRequest\Post());
}
}
protected function getPackagesXmlString()
{
$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>';
}
protected function prepare()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
return $packages;
}
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
protected function execute()
{
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->urlDev;
}
if ($pounds < 1) {
throw new Exception('Weight missing');
}
$this->response = $this->rateRequest->execute($url, $this->data);
$service_code = '03';
return $this;
}
$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>';
protected function process()
{
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
return $this;
}
$rate_list = $dom->getElementsByTagName('RatedShipment');
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
} else {
$url = $this->url_dev;
}
if (empty($rate_list->length)) {
throw new Exception('Unable to get UPS Rates.');
}
} catch (Exception $e) {
echo $this->response;
throw $e;
}
$this->response = $this->rate_request->execute($url, $this->data);
foreach ($rate_list as $rate) {
$code = @$rate
->getElementsByTagName('Service')->item(0)
->getElementsByTagName('Code')->item(0)->nodeValue;
return $this;
}
$name = Arr::get($this->shippingCodes['US'], $code);
protected function process()
{
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
$cost = @$rate
->getElementsByTagName('TotalCharges')->item(0)
->getElementsByTagName('MonetaryValue')->item(0)->nodeValue;
$rate_list = @$dom->getElementsByTagName('RatedShipment');
if (! empty($this->approvedCodes) && ! in_array($code, $this->approvedCodes)) {
continue;
}
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;
}
$quote = new Quote;
$quote
->setCarrier('ups')
->setCode($code)
->setName($name)
->setCost($cost * 100);
$this->rates[] = $quote;
}
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;
}
return $this;
}
}

View File

@ -1,182 +1,206 @@
<?php
namespace pdt256\Shipping\USPS;
use pdt256\Shipping;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use pdt256\Shipping\Validator;
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';
class Rate extends RateAdapter {
private $username = 'XXX';
private $password = 'XXX';
private $urlDev = 'http://production.shippingapis.com/ShippingAPI.dll';
private $urlProd = 'http://production.shippingapis.com/ShippingAPI.dll';
private $username;
private $password;
public $approved_codes = [
'1',
'4',
];
/**
* Codes of appropriate shipping types. Default value is specified in __construct.
*/
public $approvedCodes;
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',
],
];
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);
public function __construct($options = [])
{
parent::__construct($options);
$this->username = Arr::get($options, 'username');
$this->password = Arr::get($options, 'password');
$this->approvedCodes = Arr::get($options, 'approvedCodes', [
'1',
'4',
]);
$this->setRequestAdapter(Arr::get($options, 'requestAdapter', new RateRequest\Get()));
}
if (isset($options['username'])) {
$this->username = $options['username'];
}
protected function validate() {
$this->validatePackages();
Validator::checkIfNull($this->username, 'username');
Validator::checkIfNull($this->password, 'password');
Validator::checkIfNull($this->shipment->getFromPostalCode(), 'fromPostalCode');
Validator::checkIfNull($this->shipment->getToPostalCode(), 'toPostalCode');
if (isset($options['password'])) {
$this->password = $options['password'];
}
return $this;
}
if (isset($options['username'])) {
$this->username = $options['username'];
}
protected function prepare() {
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
$sequence_number++;
if (isset($options['approved_codes'])) {
$this->approved_codes = $options['approved_codes'];
}
/**
* 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
if (isset($options['request_adapter'])) {
$this->set_request_adapter($options['request_adapter']);
} else {
$this->set_request_adapter(new RateRequest\Get());
}
}
*/
if ($p->getWidth() > 12 or $p->getLength() > 12 or $p->getHeight() > 12) {
$size = 'LARGE';
$container = 'RECTANGULAR';
} else {
$size = 'REGULAR';
$container = 'VARIABLE';
}
protected function prepare()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
$packages .= '<Package ID="' . $sequence_number . '">' .
'<Service>ALL</Service>' .
'<ZipOrigination>' . $this->shipment->getFromPostalCode() . '</ZipOrigination>' .
'<ZipDestination>' . $this->shipment->getToPostalCode() . '</ZipDestination>' .
'<Pounds>' . $p->getPounds() . '</Pounds>' .
'<Ounces>' . $p->getOunces() . '</Ounces>' .
'<Container>' . $container . '</Container>' .
'<Size>' . $size . '</Size>' .
'<Width>' . $p->getWidth() . '</Width>' .
'<Length>' . $p->getLength() . '</Length>' .
'<Height>' . $p->getHeight() . '</Height>' .
'<Machinable>' . 'False' . '</Machinable>' .
'</Package>';
}
// https://www.usps.com/business/web-tools-apis/rate-calculators-v1-7a.htm
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
$this->data = '<RateV4Request USERID="' . $this->username . '">' .
'<Revision/>' .
$packages .
'</RateV4Request>';
if ($pounds < 1) {
throw new Exception('Weight missing');
}
return $this;
}
$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>';
protected function execute() {
if ($this->isProduction) {
$url = $this->urlProd;
} else {
$url = $this->urlDev;
}
return $this;
}
$url_request = $url . '?API=RateV4&XML=' . rawurlencode($this->data);
protected function execute()
{
if ($this->is_prod) {
$url = $this->url_prod;
} else {
$url = $this->url_dev;
}
$this->response = $this->rateRequest->execute($url_request);
$url_request = $url . '?API=RateV4&XML=' . rawurlencode($this->data);
return $this;
}
$this->response = $this->rate_request->execute($url_request);
protected function process() {
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
return $this;
}
$postage_list = @$dom->getElementsByTagName('Postage');
protected function process()
{
try {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXml($this->response);
if (empty($postage_list)) {
throw new Exception('Unable to get USPS Rates.');
}
} catch (Exception $e) {
throw $e;
}
$postage_list = @$dom->getElementsByTagName('Postage');
/** @var Quote[] $rates */
$rates = [];
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;
foreach ($postage_list as $postage) {
$code = @$postage->getAttribute('CLASSID');
$cost = @$postage->getElementsByTagName('Rate')->item(0)->nodeValue;
$name = Arr::get($this->shipping_codes['domestic'], $code);
$name = Arr::get($this->shipping_codes['domestic'], $code);
if (!empty($this->approvedCodes) && !in_array($code, $this->approvedCodes)) {
continue;
}
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
continue;
}
if (array_key_exists($code, $rates)) {
$cost = $rates[$code]->getCost() + ($cost * 100);
} else {
$cost = $cost * 100;
}
$this->rates[] = array(
'code' => $code,
'name' => $name,
'cost' => (int) ($cost * 100),
);
}
$quote = new Quote;
$quote
->setCarrier('usps')
->setCode($code)
->setName($name)
->setCost((int) $cost);
$rates[$quote->getCode()] = $quote;
}
$this->rates = array_values($rates);
return $this;
}
return $this;
}
}

12
src/Validator.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace pdt256\Shipping;
class Validator
{
public static function checkIfNull($value, $name)
{
if ($value === null) {
throw new \LogicException("$name is not set");
}
}
}

30
tests/ArrTest.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace pdt256\Shipping;
use PHPUnit\Framework\TestCase;
class ArrTest extends 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)
);
}
}

187
tests/Fedex/FedexTest.php Normal file
View File

@ -0,0 +1,187 @@
<?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;
use PHPUnit\Framework\TestCase;
class RateTest extends 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')
->setFromIsResidential(true)
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
}
public function testMockRates()
{
$rateAdapter = new Rate([
'prod' => false,
'key' => 'XXX',
'password' => 'XXX',
'accountNumber' => 'XXX',
'meterNumber' => 'XXX',
'dropOffType' => '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);
}
/**
* @expectedException \LogicException
*/
public function testMissingKey()
{
$rateAdapter = new Rate([
'prod' => false,
'password' => 'XXX',
'accountNumber' => 'XXX',
'meterNumber' => 'XXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingPassword()
{
$rateAdapter = new Rate([
'prod' => false,
'key' => 'XXX',
'accountNumber' => 'XXX',
'meterNumber' => 'XXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingAccountNumber()
{
$rateAdapter = new Rate([
'prod' => false,
'key' => 'XXX',
'password' => 'XXX',
'meterNumber' => 'XXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingMeterNumber()
{
$rateAdapter = new Rate([
'prod' => false,
'key' => 'XXX',
'password' => 'XXX',
'accountNumber' => 'XXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubFedex,
]);
$rateAdapter->getRates();
}
}

View File

@ -0,0 +1,227 @@
<?php
namespace pdt256\Shipping;
use pdt256\Shipping\RateRequest\StubFedex;
use pdt256\Shipping\RateRequest\StubUPS;
use pdt256\Shipping\RateRequest\StubUSPS;
use PHPUnit\Framework\TestCase;
class PackageDimensionsValidationTrait extends TestCase
{
protected function getNormalPackage()
{
$normalPackage = new Package();
$normalPackage
->setHeight(10)
->setWidth(6)
->setLength(10)
->setWeight(5)
;
return $normalPackage;
}
protected function getNoHeightPackage()
{
$noHeightPackage = new Package();
$noHeightPackage
->setWidth(6)
->setLength(10)
->setWeight(5)
;
return $noHeightPackage;
}
protected function getNoWidthPackage()
{
$noWidthPackage = new Package();
$noWidthPackage
->setHeight(10)
->setLength(10)
->setWeight(5)
;
return $noWidthPackage;
}
protected function getNoLengthPackage()
{
$noLengthPackage = new Package();
$noLengthPackage
->setHeight(10)
->setWidth(6)
->setWeight(5)
;
return $noLengthPackage;
}
protected function getNoWeightPackage()
{
$noWeightPackage = new Package();
$noWeightPackage
->setHeight(10)
->setWidth(10)
->setWidth(6)
;
return $noWeightPackage;
}
protected function getUSPSAdapter()
{
return new USPS\Rate([
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'requestAdapter' => new StubUSPS(),
]);
}
protected function getUPSAdapter()
{
return new UPS\Rate([
'accessKey' => 'XXX',
'userId' => 'XXX',
'password' => 'XXX',
'shipperNumber' => 'XXX',
'prod' => false,
'requestAdapter' => new StubUPS(),
]);
}
protected function getFedexAdapter()
{
return new Fedex\Rate([
'prod' => false,
'key' => 'XXX',
'password' => 'XXX',
'accountNumber' => 'XXX',
'meterNumber' => 'XXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'requestAdapter' => new StubFedex(),
]);
}
protected function validatePackage(Package $package, RateAdapter $adapter)
{
$shipment = new Shipment();
$shipment->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setFromIsResidential(true)
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
$adapter->setShipment($shipment);
$adapter->getRates();
$this->assertEquals($shipment, $adapter->getShipment());
}
public function testNormalUSPS()
{
$this->validatePackage($this->getNormalPackage(), $this->getUSPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoHeightPackageUSPS()
{
$this->validatePackage($this->getNoHeightPackage(), $this->getUSPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoLengthPackageUSPS()
{
$this->validatePackage($this->getNoLengthPackage(), $this->getUSPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWidthPackageUSPS()
{
$this->validatePackage($this->getNoWidthPackage(), $this->getUSPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWeightPackageUSPS()
{
$this->validatePackage($this->getNoWeightPackage(), $this->getUSPSAdapter());
}
public function testNormalUPS()
{
$this->validatePackage($this->getNormalPackage(), $this->getUPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoHeightPackageUPS()
{
$this->validatePackage($this->getNoHeightPackage(), $this->getUPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoLengthPackageUPS()
{
$this->validatePackage($this->getNoLengthPackage(), $this->getUPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWidthPackageUPS()
{
$this->validatePackage($this->getNoWidthPackage(), $this->getUPSAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWeightPackageUPS()
{
$this->validatePackage($this->getNoWeightPackage(), $this->getUPSAdapter());
}
public function testNormalFedex()
{
$this->validatePackage($this->getNormalPackage(), $this->getFedexAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoHeightPackageFedex()
{
$this->validatePackage($this->getNoHeightPackage(), $this->getFedexAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoLengthPackageFedex()
{
$this->validatePackage($this->getNoLengthPackage(), $this->getFedexAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWidthPackageFedex()
{
$this->validatePackage($this->getNoWidthPackage(), $this->getFedexAdapter());
}
/**
* @expectedException \LogicException
*/
public function testNoWeightPackageFedex()
{
$this->validatePackage($this->getNoWeightPackage(), $this->getFedexAdapter());
}
}

29
tests/PackageTest.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace pdt256\Shipping;
use PHPUnit\Framework\TestCase;
class PackageTest extends 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(5, $package->getPounds());
$this->assertEquals(0, $package->getOunces());
$this->assertEquals(6, $package->getWidth());
$this->assertEquals(7, $package->getLength());
$this->assertEquals(8, $package->getHeight());
$package->setPounds(3);
$package->setOunces(4);
$this->assertEquals(3, $package->getPounds());
$this->assertEquals(4, $package->getOunces());
$this->assertEquals(3.25, $package->getWeight());
}
}

26
tests/QuoteTest.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace pdt256\Shipping;
use DateTime;
use PHPUnit\Framework\TestCase;
class QuoteTest extends 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());
}
}

View File

@ -1,425 +1,157 @@
<?php
use pdt256\Shipping\Ship;
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;
use PHPUnit\Framework\TestCase;
class ShipTest extends 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,
],
];
/** @var Shipment */
public $shipment;
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',
],
],
];
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');
public function setUp()
{
$s = new Shipment;
$s->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setFromIsResidential(true)
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true);
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(),
];
}
$p = new Package;
$p->setWeight(3)
->setWidth(9)
->setLength(9)
->setHeight(9);
private function getUPSOptions()
{
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('ups');
$s->addPackage($p);
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(),
];
}
$this->shipment = $s;
}
private function getFedexOptions()
{
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('fedex');
private function getUSPSOptions()
{
$ship = Ship::factory($this->shipping_options);
$approvedCodes = $ship->getApprovedCodes('usps');
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(),
];
}
return [
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => $this->shipment,
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubUSPS(),
];
}
public function testUSPSRate()
{
$usps = new USPS\Rate($this->getUSPSOptions());
$usps_rates = $usps->get_rates();
private function getUPSOptions()
{
$ship = Ship::factory($this->shipping_options);
$approvedCodes = $ship->getApprovedCodes('ups');
$this->assertEquals(json_encode([
1 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1001,
],
0 => [
'code' => '1',
'name' => 'Priority Mail',
'cost' => 1220,
],
]), json_encode($usps_rates));
}
return [
'prod' => false,
'accessKey' => 'XXXX',
'userId' => 'XXXX',
'password' => 'XXXX',
'shipperNumber' => 'XXXX',
'shipment' => $this->shipment,
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubUPS(),
];
}
public function testUPSRate()
{
$ups = new UPS\Rate($this->getUPSOptions());
$ups_rates = $ups->get_rates();
private function getFedexOptions()
{
$ship = Ship::factory($this->shipping_options);
$approvedCodes = $ship->getApprovedCodes('fedex');
$this->assertEquals(json_encode([
0 => [
'code' => '03',
'name' => 'UPS Ground',
'cost' => 1910,
],
1 => [
'code' => '02',
'name' => 'UPS 2nd Day Air',
'cost' => 4923,
],
2 => [
'code' => '13',
'name' => 'UPS Next Day Air Saver',
'cost' => 8954,
],
3 => [
'code' => '01',
'name' => 'UPS Next Day Air',
'cost' => 9328,
],
]), json_encode($ups_rates));
}
return [
'prod' => false,
'key' => 'XXXX',
'password' => 'XXXX',
'accountNumber' => 'XXXX',
'meterNumber' => 'XXXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $this->shipment,
'approvedCodes' => $approvedCodes,
'requestAdapter' => new RateRequest\StubFedex(),
];
}
public function testFedexRate()
{
$fedex = new Fedex\Rate($this->getFedexOptions());
$fedex_rates = $fedex->get_rates();
public function testDisplayOptions()
{
$rates = [];
$this->assertEquals(json_encode([
3 => [
'code' => 'GROUND_HOME_DELIVERY',
'name' => 'Ground Home Delivery',
'cost' => 1655,
'delivery_ts' => NULL,
'transit_time' => 'THREE_DAYS',
],
2 => [
'code' => 'FEDEX_EXPRESS_SAVER',
'name' => 'Fedex Express Saver',
'cost' => 2989,
'delivery_ts' => '2014-09-30T20:00:00',
'transit_time' => NULL,
],
1 => [
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4072,
'delivery_ts' => '2014-09-29T20:00:00',
'transit_time' => NULL,
],
0 => [
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7834,
'delivery_ts' => '2014-09-26T20:00:00',
'transit_time' => NULL,
],
]), json_encode($fedex_rates));
}
$usps = new USPS\Rate($this->getUSPSOptions());
$rates['usps'] = $usps->getRates();
public function testDisplayOptions()
{
$rates = [];
$ups = new UPS\Rate($this->getUPSOptions());
$rates['ups'] = $ups->getRates();
$usps = new USPS\Rate($this->getUSPSOptions());
$rates['usps'] = $usps->get_rates();
$fedex = new Fedex\Rate($this->getFedexOptions());
$rates['fedex'] = $fedex->getRates();
$ups = new UPS\Rate($this->getUPSOptions());
$rates['ups'] = $ups->get_rates();
$ship = Ship::factory($this->shipping_options);
$rates = $ship->getDisplayRates($rates);
$fedex = new Fedex\Rate($this->getFedexOptions());
$rates['fedex'] = $fedex->get_rates();
$post = new Quote('usps', '4', 'Parcel Post', 1001);
$ship = Ship::factory($this->shipping_options);
$display_rates = $ship->get_display_rates($rates);
$fedexTwoDay = new Quote('fedex', 'FEDEX_2_DAY', 'Fedex 2 Day', 4072);
$fedexTwoDay->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'));
$this->assertEquals(json_encode([
'Standard Shipping' => [
0 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1001,
'carrier' => 'usps',
],
],
'Two-Day Shipping' => [
0 => [
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4072,
'delivery_ts' => '2014-09-29T20:00:00',
'transit_time' => NULL,
'carrier' => 'fedex',
],
],
'One-Day Shipping' => [
0 => [
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7834,
'delivery_ts' => '2014-09-26T20:00:00',
'transit_time' => NULL,
'carrier' => 'fedex',
],
],
]), json_encode($display_rates));
}
$overnight = new Quote('fedex', 'STANDARD_OVERNIGHT', 'Standard Overnight', 7834);
$overnight->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'));
/**
* @expectedException Exception
*/
public function testUSPSRateMissingTo()
{
$usps_options = $this->getUSPSOptions();
unset($usps_options['shipment']['to']);
$expected = [
'Standard Shipping' => [
$post,
],
'Two-Day Shipping' => [
$fedexTwoDay,
],
'One-Day Shipping' => [
$overnight,
],
];
$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);
// }
$this->assertEquals($expected, $rates);
}
}

129
tests/ShipmentTest.php Normal file
View File

@ -0,0 +1,129 @@
<?php
namespace pdt256\Shipping;
use PHPUnit\Framework\TestCase;
class ShipmentTest extends 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());
}
private function getShipment()
{
$shipment = new Shipment;
$shipment
->setFromIsResidential(false)
->setFromStateProvinceCode('IN')
->setFromPostalCode('46205')
->setFromCountryCode('US')
->setToIsResidential(true)
->setToPostalCode('20101')
->setToCountryCode('US');
$package = new Package;
$package
->setLength(12)
->setWidth(4)
->setHeight(3)
->setWeight(3);
$shipment->addPackage($package);
return $shipment;
}
public function xtestUPSStubForReadme()
{
$shipment = $this->getShipment();
$ups = new UPS\Rate([
'prod' => false,
'accessKey' => 'XXXX',
'userId' => 'XXXX',
'password' => 'XXXX',
'shipperNumber' => 'XXXX',
'shipment' => $shipment,
'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
],
'requestAdapter' => new RateRequest\StubUPS(),
]);
$rates = $ups->getRates();
var_export($rates);
}
public function xtestUSPSStubForReadme()
{
$shipment = $this->getShipment();
$usps = new USPS\Rate([
'prod' => false,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => $shipment,
'approvedCodes' => [
'1', // 1-3 business days
'4', // 2-8 business days
],
'requestAdapter' => new RateRequest\StubUSPS(),
]);
$rates = $usps->getRates();
var_export($rates);
}
public function xtestFedexStubForReadme()
{
$shipment = $this->getShipment();
$fedex = new Fedex\Rate([
'prod' => false,
'key' => 'XXXX',
'password' => 'XXXX',
'accountNumber' => 'XXXX',
'meterNumber' => 'XXXX',
'dropOffType' => 'BUSINESS_SERVICE_CENTER',
'shipment' => $shipment,
'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
],
'requestAdapter' => new RateRequest\StubFedex(),
]);
$rates = $fedex->getRates();
var_export($rates);
}
}

156
tests/UPS/RateTest.php Normal file
View File

@ -0,0 +1,156 @@
<?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;
use PHPUnit\Framework\TestCase;
class RateTest extends 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')
->setFromIsResidential(true)
->setToPostalCode('78703')
->setToCountryCode('US')
->setToIsResidential(true)
->addPackage($package);
}
public function testMockRates()
{
$rateAdapter = new Rate([
'accessKey' => 'XXX',
'userId' => 'XXX',
'password' => 'XXX',
'shipperNumber' => 'XXX',
'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);
}
/**
* @expectedException \LogicException
*/
public function testMissingAccessKey()
{
$rateAdapter = new Rate([
'userId' => 'XXX',
'password' => 'XXX',
'shipperNumber' => 'XXX',
'prod' => false,
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUPS,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingPassword()
{
$rateAdapter = new Rate([
'accessKey' => 'XXX',
'userId' => 'XXX',
'shipperNumber' => 'XXX',
'prod' => false,
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUPS,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingShipperNumber()
{
$rateAdapter = new Rate([
'accessKey' => 'XXX',
'userId' => 'XXX',
'password' => 'XXX',
'prod' => false,
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUPS,
]);
$rateAdapter->getRates();
}
}

118
tests/USPS/RateTest.php Normal file
View File

@ -0,0 +1,118 @@
<?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;
use PHPUnit\Framework\TestCase;
class RateTest extends 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);
}
/**
* @expectedException \LogicException
*/
public function testMissingUserName()
{
$rateAdapter = new Rate([
'prod' => false,
'password' => 'XXXX',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUSPS,
]);
$rateAdapter->getRates();
}
/**
* @expectedException \LogicException
*/
public function testMissingPassword()
{
$rateAdapter = new Rate([
'prod' => false,
'username' => 'XXX',
'shipment' => $this->shipment,
'approvedCodes' => $this->approvedCodes,
'requestAdapter' => new StubUSPS,
]);
$rateAdapter->getRates();
}
}

26
tests/ValidatorTest.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace pdt256\Shipping;
use PHPUnit\Framework\TestCase;
use stdClass;
class ValidatorTest extends TestCase
{
/**
* @expectedException \LogicException
*/
public function testNull()
{
Validator::checkIfNull(null, 'null');
}
public function testNotNull()
{
Validator::checkIfNull('XXX', 'notNullValue');
Validator::checkIfNull([], 'notNullValue');
Validator::checkIfNull(new stdClass(), 'notNullValue');
Validator::checkIfNull(function () {
}, 'notNullValue');
$this->assertTrue(true);
}
}