You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
7.1 KiB
PHP
237 lines
7.1 KiB
PHP
<?php
|
|
namespace pdt256\Shipping\Fedex;
|
|
|
|
use pdt256\Shipping;
|
|
use pdt256\Shipping\Arr;
|
|
use pdt256\Shipping\RateAdapter;
|
|
use pdt256\Shipping\RateRequest;
|
|
use DOMDocument;
|
|
use Exception;
|
|
|
|
class Rate extends RateAdapter
|
|
{
|
|
private $url_dev = 'https://gatewaybeta.fedex.com/web-services/';
|
|
private $url_prod = 'https://gateway.fedex.com/web-services/';
|
|
|
|
private $key = 'XXX';
|
|
private $password = 'XXX';
|
|
private $account_number = 'XXX';
|
|
private $meter_number = 'XXX';
|
|
private $drop_off_type = 'BUSINESS_SERVICE_CENTER';
|
|
|
|
public $approved_codes = [
|
|
'PRIORITY_OVERNIGHT',
|
|
'FEDEX_2_DAY',
|
|
'FEDEX_EXPRESS_SAVER',
|
|
'FEDEX_GROUND',
|
|
'GROUND_HOME_DELIVERY',
|
|
];
|
|
|
|
private $shipping_codes = [
|
|
'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => 'Europe First International Priority',
|
|
'FEDEX_1_DAY_FREIGHT' => 'Fedex 1 Day Freight',
|
|
'FEDEX_2_DAY' => 'Fedex 2 Day',
|
|
'FEDEX_2_DAY_AM' => 'Fedex 2 Day AM',
|
|
'FEDEX_2_DAY_FREIGHT' => 'Fedex 2 Day Freight',
|
|
'FEDEX_3_DAY_FREIGHT' => 'Fedex 3 Day Freight',
|
|
'FEDEX_EXPRESS_SAVER' => 'Fedex Express Saver',
|
|
'FEDEX_FIRST_FREIGHT' => 'Fedex First Freight',
|
|
'FEDEX_FREIGHT_ECONOMY' => 'Fedex Freight Economy',
|
|
'FEDEX_FREIGHT_PRIORITY' => 'Fedex Freight Priority',
|
|
'FEDEX_GROUND' => 'Fedex Ground',
|
|
'FIRST_OVERNIGHT' => 'First Overnight',
|
|
'GROUND_HOME_DELIVERY' => 'Ground Home Delivery',
|
|
'INTERNATIONAL_ECONOMY' => 'International Economy',
|
|
'INTERNATIONAL_ECONOMY_FREIGHT' => 'International Economy Freight',
|
|
'INTERNATIONAL_FIRST' => 'International First',
|
|
'INTERNATIONAL_PRIORITY' => 'International Priority',
|
|
'INTERNATIONAL_PRIORITY_FREIGHT' => 'International Priority Freight',
|
|
'PRIORITY_OVERNIGHT' => 'Priority Overnight',
|
|
'SMART_POST' => 'Smart Post',
|
|
'STANDARD_OVERNIGHT' => 'Standard Overnight',
|
|
];
|
|
|
|
public function __construct($options = [])
|
|
{
|
|
parent::__construct($options);
|
|
|
|
if (isset($options['key'])) {
|
|
$this->key = $options['key'];
|
|
}
|
|
|
|
if (isset($options['password'])) {
|
|
$this->password = $options['password'];
|
|
}
|
|
|
|
if (isset($options['account_number'])) {
|
|
$this->account_number = $options['account_number'];
|
|
}
|
|
|
|
if (isset($options['meter_number'])) {
|
|
$this->meter_number = $options['meter_number'];
|
|
}
|
|
|
|
if (isset($options['approved_codes'])) {
|
|
$this->approved_codes = $options['approved_codes'];
|
|
}
|
|
|
|
if (isset($options['drop_off_type'])) {
|
|
$this->drop_off_type = $options['drop_off_type'];
|
|
}
|
|
|
|
if (isset($options['request_adapter'])) {
|
|
$this->set_request_adapter($options['request_adapter']);
|
|
} else {
|
|
$this->set_request_adapter(new RateRequest\Post());
|
|
}
|
|
}
|
|
|
|
protected function prepare()
|
|
{
|
|
$to = Arr::get($this->shipment, 'to');
|
|
$shipper = Arr::get($this->shipment, 'from');
|
|
$dimensions = Arr::get($this->shipment, 'dimensions');
|
|
|
|
$pounds = (int) Arr::get($this->shipment, 'weight');
|
|
$ounces = 0;
|
|
|
|
if ($pounds < 1) {
|
|
throw new Exception('Weight missing');
|
|
}
|
|
|
|
$date = time();
|
|
$day_name = date('l', $date);
|
|
|
|
if ($day_name == 'Saturday') {
|
|
$date += 172800;
|
|
} elseif ($day_name == 'Sunday') {
|
|
$date += 86400;
|
|
}
|
|
|
|
// http://www.fedex.com/templates/components/apps/wpor/secure/downloads/pdf/Aug13/PropDevGuide.pdf
|
|
// http://www.fedex.com/us/developer/product/WebServices/MyWebHelp_August2010/Content/Proprietary_Developer_Guide/Rate_Services_conditionalized.htm
|
|
|
|
$this->data =
|
|
'<?xml version="1.0"?>
|
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://fedex.com/ws/rate/v13">
|
|
<SOAP-ENV:Body>
|
|
<RateRequest>
|
|
<WebAuthenticationDetail>
|
|
<UserCredential>
|
|
<Key>' . $this->key . '</Key>
|
|
<Password>' . $this->password . '</Password>
|
|
</UserCredential>
|
|
</WebAuthenticationDetail>
|
|
<ClientDetail>
|
|
<AccountNumber>' . $this->account_number . '</AccountNumber>
|
|
<MeterNumber>' . $this->meter_number . '</MeterNumber>
|
|
</ClientDetail>
|
|
<Version>
|
|
<ServiceId>crs</ServiceId>
|
|
<Major>13</Major>
|
|
<Intermediate>0</Intermediate>
|
|
<Minor>0</Minor>
|
|
</Version>
|
|
<ReturnTransitAndCommit>true</ReturnTransitAndCommit>
|
|
<RequestedShipment>
|
|
<ShipTimestamp>' . date('c') . '</ShipTimestamp>
|
|
<DropoffType>' . $this->drop_off_type . '</DropoffType>
|
|
<PackagingType>' . Arr::get($this->shipment, 'packaging_type') . '</PackagingType>
|
|
<Shipper>
|
|
<Address>
|
|
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
|
|
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
|
|
' . ((Arr::get($shipper, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
|
|
</Address>
|
|
</Shipper>
|
|
<Recipient>
|
|
<Address>
|
|
<PostalCode>' . Arr::get($to, 'postal_code') . '</PostalCode>
|
|
<CountryCode>' . Arr::get($to, 'country_code') . '</CountryCode>
|
|
' . ((Arr::get($to, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
|
|
</Address>
|
|
</Recipient>
|
|
<RateRequestTypes>LIST</RateRequestTypes>
|
|
<PackageCount>1</PackageCount>
|
|
<RequestedPackageLineItems>
|
|
<SequenceNumber>1</SequenceNumber>
|
|
<GroupPackageCount>1</GroupPackageCount>
|
|
<Weight>
|
|
<Units>LB</Units>
|
|
<Value>' . $pounds . '</Value>
|
|
</Weight>
|
|
<Dimensions>
|
|
<Length>' . Arr::get($dimensions, 'length') . '</Length>
|
|
<Width>' . Arr::get($dimensions, 'width') . '</Width>
|
|
<Height>' . Arr::get($dimensions, 'height') . '</Height>
|
|
<Units>IN</Units>
|
|
</Dimensions>
|
|
</RequestedPackageLineItems>
|
|
</RequestedShipment>
|
|
</RateRequest>
|
|
</SOAP-ENV:Body>
|
|
</SOAP-ENV:Envelope>';
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function execute()
|
|
{
|
|
if ($this->is_prod) {
|
|
$url = $this->url_prod;
|
|
} else {
|
|
$url = $this->url_dev;
|
|
}
|
|
|
|
$this->response = $this->rate_request->execute($url, $this->data);
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function process()
|
|
{
|
|
try {
|
|
$dom = new DOMDocument('1.0', 'UTF-8');
|
|
$dom->loadXml($this->response);
|
|
$rate_reply = $dom->getElementsByTagName('RateReplyDetails');
|
|
|
|
if (empty($rate_reply->length)) {
|
|
throw new Exception('Unable to get FedEx Rates.');
|
|
}
|
|
} catch (Exception $e) {
|
|
// StatsD::increment('error.shipping.get_fedex_rate');
|
|
// Kohana::$log->add(Log::ERROR, $e)->write();
|
|
throw $e;
|
|
}
|
|
|
|
foreach ($rate_reply as $rate) {
|
|
$code = $rate->getElementsByTagName('ServiceType')->item(0)->nodeValue;
|
|
|
|
if ( ! empty($this->approved_codes) AND ! in_array($code, $this->approved_codes)) {
|
|
continue;
|
|
}
|
|
|
|
$name = Arr::get($this->shipping_codes, $code);
|
|
|
|
$delivery_ts = @$rate->getElementsByTagName('DeliveryTimestamp')->item(0)->nodeValue;
|
|
$transit_time = @$rate->getElementsByTagName('TransitTime')->item(0)->nodeValue;
|
|
|
|
$cost = $rate
|
|
->getElementsByTagName('RatedShipmentDetails')->item(0)
|
|
->getElementsByTagName('ShipmentRateDetail')->item(0)
|
|
->getElementsByTagName('TotalNetCharge')->item(0)
|
|
->getElementsByTagName('Amount')->item(0)->nodeValue;
|
|
|
|
$this->rates[] = array(
|
|
'code' => $code,
|
|
'name' => $name,
|
|
'cost' => (int) ($cost * 100),
|
|
'delivery_ts' => $delivery_ts,
|
|
'transit_time' => $transit_time,
|
|
);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
}
|