Buildateam_Shippo - Version 1.0.2

Version Notes

Don't forget to specify Weight and Dimensions for each Product and to enable Shippo rates here:
System > Configuration > Sales > ShippingMethods > Shippo

Download this release

Release Info

Developer Buildateam
Extension Buildateam_Shippo
Version 1.0.2
Comparing to
See all releases


Version 1.0.2

Files changed (46) hide show
  1. app/code/community/Buildateam/Shippo/Block/Adminhtml/Frontend/Region/Updater.php +47 -0
  2. app/code/community/Buildateam/Shippo/Helper/Carrier.php +27 -0
  3. app/code/community/Buildateam/Shippo/Helper/Data.php +230 -0
  4. app/code/community/Buildateam/Shippo/Helper/Shippo.php +205 -0
  5. app/code/community/Buildateam/Shippo/Model/Carrier.php +121 -0
  6. app/code/community/Buildateam/Shippo/Model/Observer.php +5 -0
  7. app/code/community/Buildateam/Shippo/Model/Parcel.php +71 -0
  8. app/code/community/Buildateam/Shippo/Model/System/Config/Source/Region.php +7 -0
  9. app/code/community/Buildateam/Shippo/Model/System/Config/Source/Size.php +21 -0
  10. app/code/community/Buildateam/Shippo/Model/System/Config/Source/Weight.php +21 -0
  11. app/code/community/Buildateam/Shippo/controllers/TestController.php +38 -0
  12. app/code/community/Buildateam/Shippo/etc/adminhtml.xml +23 -0
  13. app/code/community/Buildateam/Shippo/etc/config.xml +118 -0
  14. app/code/community/Buildateam/Shippo/etc/system.xml +185 -0
  15. app/code/community/Buildateam/Shippo/sql/buildateam_shippo_setup/mysql4-upgrade-1.0.0-1.0.1.php +7 -0
  16. app/code/community/Buildateam/Shippo/sql/buildateam_shippo_setup/mysql4-upgrade-1.0.1-1.0.2.php +32 -0
  17. app/design/adminhtml/default/default/layout/buildateam/shippo.xml +16 -0
  18. app/design/adminhtml/default/default/template/buildateam/shippo/catalog/product/js.phtml +15 -0
  19. app/etc/modules/Buildateam_Shippo.xml +10 -0
  20. lib/Shippo.php +47 -0
  21. lib/Shippo/Address.php +61 -0
  22. lib/Shippo/ApiConnectionError.php +5 -0
  23. lib/Shippo/ApiError.php +5 -0
  24. lib/Shippo/ApiRequestor.php +281 -0
  25. lib/Shippo/ApiResource.php +139 -0
  26. lib/Shippo/AttachedObject.php +23 -0
  27. lib/Shippo/AuthenticationError.php +5 -0
  28. lib/Shippo/CarrierAccount.php +62 -0
  29. lib/Shippo/CustomsDeclaration.php +50 -0
  30. lib/Shippo/CustomsItem.php +50 -0
  31. lib/Shippo/Error.php +27 -0
  32. lib/Shippo/InvalidRequestError.php +10 -0
  33. lib/Shippo/List.php +29 -0
  34. lib/Shippo/Manifest.php +38 -0
  35. lib/Shippo/Object.php +262 -0
  36. lib/Shippo/Parcel.php +38 -0
  37. lib/Shippo/Rate.php +26 -0
  38. lib/Shippo/RateLimitError.php +9 -0
  39. lib/Shippo/Refund.php +38 -0
  40. lib/Shippo/Shipment.php +50 -0
  41. lib/Shippo/Shippo.php +57 -0
  42. lib/Shippo/SingletonApiResource.php +31 -0
  43. lib/Shippo/Transaction.php +38 -0
  44. lib/Shippo/Util.php +78 -0
  45. lib/Shippo/Util/Set.php +34 -0
  46. package.xml +19 -0
app/code/community/Buildateam/Shippo/Block/Adminhtml/Frontend/Region/Updater.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Block_Adminhtml_Frontend_Region_Updater
4
+ extends Mage_Adminhtml_Block_System_Config_Form_Field
5
+ {
6
+ protected $_idCountry = 'carriers_buildateam_shippo_from_country';
7
+
8
+ protected $_idRegion = 'carriers_buildateam_shippo_from_region';
9
+
10
+ public function getCountryElementId()
11
+ {
12
+ return $this->_idCountry;
13
+ }
14
+
15
+ public function getRegionElementId()
16
+ {
17
+ return $this->_idRegion;
18
+ }
19
+
20
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
21
+ {
22
+ $html = parent::_getElementHtml($element);
23
+
24
+ $idCountry = $this->getCountryElementId();
25
+ $idRegion = $this->getRegionElementId();
26
+
27
+ $js = '<script type="text/javascript">
28
+ var updater = new RegionUpdater("' . $idCountry . '", "none", "' . $idRegion . '", %s, "nullify");
29
+ if(updater.lastCountryId) {
30
+ var tmpRegionId = $("' . $idRegion . '").value;
31
+ var tmpCountryId = updater.lastCountryId;
32
+ updater.lastCountryId=false;
33
+ updater.update();
34
+ updater.lastCountryId = tmpCountryId;
35
+ $("' . $idRegion .'").value = tmpRegionId;
36
+ } else {
37
+ updater.update();
38
+ }
39
+ </script>';
40
+
41
+ $html .= sprintf($js, $this->helper('directory')->getRegionJson());
42
+ return $html;
43
+ }
44
+ }
45
+
46
+
47
+
app/code/community/Buildateam/Shippo/Helper/Carrier.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Helper_Carrier extends Mage_Core_Helper_Abstract
4
+ {
5
+ public function getQuoteFromRateRequest(Mage_Shipping_Model_Rate_Request $request)
6
+ {
7
+ if ( count($request->getAllItems()) )
8
+ {
9
+ $firstItem = current($request->getAllItems());
10
+ $quote = $firstItem->getQuote();
11
+ return $quote;
12
+ }
13
+
14
+ return null;
15
+ }
16
+
17
+ public function getShippingAddressFromRateRequest(Mage_Shipping_Model_Rate_Request $request)
18
+ {
19
+ if ( $quote = $this->getQuoteFromRateRequest($request) )
20
+ {
21
+ $shippingAddress = $quote->getShippingAddress();
22
+ return $shippingAddress;
23
+ }
24
+
25
+ return null;
26
+ }
27
+ }
app/code/community/Buildateam/Shippo/Helper/Data.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ const LOG_FILE = 'buildateam_shippo.log';
6
+
7
+ const PATH_ECONOMY_DESCRIPTION = 'carriers/buildateam_shippo/economy_description';
8
+ const PATH_EXPEDITED_DESCRIPTION = 'carriers/buildateam_shippo/expedited_description';
9
+ const PATH_OVERNIGHT_DESCRIPTION = 'carriers/buildateam_shippo/overnight_description';
10
+ const PATH_EXPEDITED_METHOD_COST = 'carriers/buildateam_shippo/expedited_cost';
11
+ const PATH_OVERNIGHT_METHOD_COST = 'carriers/buildateam_shippo/overnight_cost';
12
+ const NOTIFY_EMAIL_RECIPIENT = 'carriers/buildateam_shippo/notify_email_recipient';
13
+
14
+ const PATH_EMAIL_DELAY_TIME = 'buildateam_shippo/general/email_delay';
15
+ const PATH_EMAIL_DELAY_ENABLED = 'buildateam_shippo/general/enable';
16
+
17
+
18
+ const PATH_FROM_PRIVATE_AUTH_TOKEN = 'carriers/buildateam_shippo/private_auth_token';
19
+
20
+ const PATH_FROM_NAME = 'carriers/buildateam_shippo/from_name';
21
+ const PATH_FROM_COMPANY = 'carriers/buildateam_shippo/from_company';
22
+ const PATH_FROM_COUNTRY = 'carriers/buildateam_shippo/from_country';
23
+ const PATH_FROM_REGION = 'carriers/buildateam_shippo/from_region';
24
+ const PATH_FROM_POSTCODE = 'carriers/buildateam_shippo/from_postcode';
25
+ const PATH_FROM_CITY = 'carriers/buildateam_shippo/from_city';
26
+ const PATH_FROM_STREET = 'carriers/buildateam_shippo/from_street';
27
+ const PATH_FROM_PHONE = 'carriers/buildateam_shippo/from_phone';
28
+ const PATH_FROM_EMAIL = 'carriers/buildateam_shippo/from_email';
29
+
30
+ const PATH_WEIGHT_UNIT = 'carriers/buildateam_shippo/weight_unit';
31
+ const PATH_SIZE_UNIT = 'carriers/buildateam_shippo/size_unit';
32
+ const PATH_HANDLING = 'carriers/buildateam_shippo/shipping_handling';
33
+
34
+
35
+ protected static $methods = array(
36
+ 'tablerate_bestway' => self::PATH_ECONOMY_DESCRIPTION,
37
+ 'buildateam_shippo_expedited' => self::PATH_EXPEDITED_DESCRIPTION,
38
+ 'buildateam_shippo_overnight' => self::PATH_OVERNIGHT_DESCRIPTION,
39
+ );
40
+
41
+
42
+ public function getPrivateAuthToken($store = null)
43
+ {
44
+ return Mage::getStoreConfig(self::PATH_FROM_PRIVATE_AUTH_TOKEN, $store);
45
+ }
46
+
47
+ public function getFromName($store = null)
48
+ {
49
+ return Mage::getStoreConfig(self::PATH_FROM_NAME, $store);
50
+ }
51
+
52
+ public function getFromCompany($store = null)
53
+ {
54
+ return Mage::getStoreConfig(self::PATH_FROM_COMPANY, $store);
55
+ }
56
+
57
+ public function getFromCountry($store = null)
58
+ {
59
+ return Mage::getStoreConfig(self::PATH_FROM_COUNTRY, $store);
60
+ }
61
+
62
+ public function getFromRegion($store = null)
63
+ {
64
+ return Mage::getStoreConfig(self::PATH_FROM_REGION, $store);
65
+ }
66
+
67
+ public function getFromPostcode($store = null)
68
+ {
69
+ return Mage::getStoreConfig(self::PATH_FROM_POSTCODE, $store);
70
+ }
71
+
72
+ public function getFromCity($store = null)
73
+ {
74
+ return Mage::getStoreConfig(self::PATH_FROM_CITY, $store);
75
+ }
76
+
77
+ public function getFromStreet($store = null)
78
+ {
79
+ return Mage::getStoreConfig(self::PATH_FROM_STREET, $store);
80
+ }
81
+
82
+ public function getFromPhone($store = null)
83
+ {
84
+ return Mage::getStoreConfig(self::PATH_FROM_PHONE, $store);
85
+ }
86
+
87
+ public function getFromEmail($store = null)
88
+ {
89
+ return Mage::getStoreConfig(self::PATH_FROM_EMAIL, $store);
90
+ }
91
+
92
+
93
+ public function getWeightUnit($store = null)
94
+ {
95
+ return Mage::getStoreConfig(self::PATH_WEIGHT_UNIT, $store);
96
+ }
97
+
98
+ public function getSizeUnit($store = null)
99
+ {
100
+ return Mage::getStoreConfig(self::PATH_SIZE_UNIT, $store);
101
+ }
102
+
103
+ public function getHandling($store = null)
104
+ {
105
+ return Mage::getStoreConfig(self::PATH_HANDLING, $store);
106
+ }
107
+
108
+ /**
109
+ * Returns economy shipping method description.
110
+ *
111
+ * @param null $store
112
+ * @return mixed
113
+ */
114
+ public function getEconomyDescription($store = null)
115
+ {
116
+ return Mage::getStoreConfig(self::PATH_ECONOMY_DESCRIPTION, $store);
117
+ }
118
+
119
+ /**
120
+ * Returns expedited shipping method description.
121
+ *
122
+ * @param null $store
123
+ * @return mixed
124
+ */
125
+ public function getExpeditedDescription($store = null)
126
+ {
127
+ return Mage::getStoreConfig(self::PATH_EXPEDITED_DESCRIPTION, $store);
128
+ }
129
+
130
+ /**
131
+ * Returns overnight shipping method description.
132
+ *
133
+ * @param null $store
134
+ * @return mixed
135
+ */
136
+ public function getOvernightDescription($store = null)
137
+ {
138
+ return Mage::getStoreConfig(self::PATH_OVERNIGHT_DESCRIPTION, $store);
139
+ }
140
+
141
+ /**
142
+ * Returns exppedited shipping method cost.
143
+ *
144
+ * @param null $store
145
+ * @return float
146
+ */
147
+ public function getExpeditedMethodCost($store = null)
148
+ {
149
+ return (float)Mage::getStoreConfig(self::PATH_EXPEDITED_METHOD_COST, $store);
150
+ }
151
+
152
+ /**
153
+ * Returns overnight shipping method cost.
154
+ *
155
+ * @param null $store
156
+ * @return float
157
+ */
158
+ public function getOvernightMethodCost($store = null)
159
+ {
160
+ return (float)Mage::getStoreConfig(self::PATH_OVERNIGHT_METHOD_COST, $store);
161
+ }
162
+
163
+ /**
164
+ * Checks if we can show description.
165
+ *
166
+ * @param array $rates
167
+ * @return bool
168
+ */
169
+ public function canShowDescription($rates)
170
+ {
171
+ if (!is_array($rates)) {
172
+ return false;
173
+ }
174
+ return array_key_exists(Buildateam_Shippo_Model_Carrier::CODE, $rates);
175
+ }
176
+
177
+ /**
178
+ * Returns description for shipping method.
179
+ *
180
+ * @param string $code
181
+ * @return string|null
182
+ */
183
+ public function getDescription($code)
184
+ {
185
+ if (!array_key_exists($code, self::$methods)) {
186
+ return;
187
+ }
188
+
189
+ return Mage::getStoreConfig(self::$methods[$code]);
190
+ }
191
+
192
+ /**
193
+ * Returns notify email recipient
194
+ *
195
+ * @param null $store
196
+ * @return string|null
197
+ */
198
+ public function getEmailRecipient($store = null)
199
+ {
200
+ return (string)Mage::getStoreConfig(self::NOTIFY_EMAIL_RECIPIENT, $store);
201
+ }
202
+
203
+
204
+ /**
205
+ * Returns shipping email delay time (when we create it using SOAP request).
206
+ *
207
+ * @param null $store
208
+ * @return float
209
+ */
210
+ public function getEmailDelayTime($store = null)
211
+ {
212
+ return (int)Mage::getStoreConfig(self::PATH_EMAIL_DELAY_TIME, $store);
213
+ }
214
+
215
+ /**
216
+ * Returns if delay is enabled for shipping confirmation emails (when we create shipment using SOAP request)
217
+ *
218
+ * @param null $store
219
+ * @return bool
220
+ */
221
+ public function getEmailDelayEnabled($store = null)
222
+ {
223
+ return Mage::getStoreConfigFlag(self::PATH_EMAIL_DELAY_ENABLED, $store);
224
+ }
225
+
226
+ public function log($message)
227
+ {
228
+ Mage::log($message, null, self::LOG_FILE);
229
+ }
230
+ }
app/code/community/Buildateam/Shippo/Helper/Shippo.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Helper_Shippo extends Mage_Core_Helper_Abstract
4
+ {
5
+ public function getRates(Mage_Shipping_Model_Rate_Request $request)
6
+ {
7
+ // Replace <API-KEY> with your credentials
8
+ # From goshippo.com > API > API Keys > Private Auth Token
9
+ //$devPrivateAuthToken = '167263307a16e8b1c51fbf4b055decc85b683678'; # DEV
10
+ Shippo::setApiKey( Mage::helper('buildateam_shippo')->getPrivateAuthToken() );
11
+
12
+ /*
13
+ // example fromAddress
14
+ $fromAddress = array(
15
+ 'object_purpose' => 'PURCHASE',
16
+ 'name' => 'Mr Hippo"',
17
+ 'company' => 'Shippo',
18
+ 'street1' => '215 Clayton St.',
19
+ 'city' => 'San Francisco',
20
+ 'state' => 'CA',
21
+ 'zip' => '94117',
22
+ 'country' => 'US',
23
+ 'phone' => '+1 555 341 9393',
24
+ 'email' => 'mr-hippo@goshipppo.com'
25
+ );
26
+
27
+
28
+ // example fromAddress
29
+ $toAddress = array(
30
+ 'object_purpose' => 'PURCHASE',
31
+ 'name' => 'Ms Hippo"',
32
+ 'company' => 'San Diego Zoo"',
33
+ 'street1' => '2920 Zoo Drive"',
34
+ 'city' => 'San Diego',
35
+ 'state' => 'CA',
36
+ 'zip' => '92101',
37
+ 'country' => 'US',
38
+ 'phone' => '+1 555 341 9393',
39
+ 'email' => 'ms-hippo@goshipppo.com'
40
+ );
41
+
42
+ // example parcel
43
+ $parcel = array(
44
+ 'length'=> '5',
45
+ 'width'=> '5',
46
+ 'height'=> '5',
47
+ 'distance_unit'=> 'in',
48
+ 'weight'=> '2',
49
+ 'mass_unit'=> 'lb',
50
+ );
51
+ */
52
+
53
+ $fromAddress = $this->prepareFromAddress();
54
+ $mageShippingAddress = Mage::helper('buildateam_shippo/carrier')->getShippingAddressFromRateRequest($request);
55
+ $toAddress = $this->prepareToAddress($mageShippingAddress);
56
+ Mage::helper('buildateam_shippo')->log('1 toAddress');
57
+ Mage::helper('buildateam_shippo')->log($toAddress);
58
+ $parcel = $this->prepareParcel($request);
59
+
60
+ // example Shipment object
61
+ $shipment = Shippo_Shipment::create(array(
62
+ 'object_purpose' => 'PURCHASE',
63
+ 'address_from' => $fromAddress,
64
+ 'address_to' => $toAddress,
65
+ 'parcel' => $parcel,
66
+ 'async' => false
67
+ ));
68
+
69
+ // Select the rate you want to purchase.
70
+ // We simply select the first rate in this example.
71
+ //$rate = $shipment["rates_list"][0];
72
+
73
+ Mage::helper('buildateam_shippo')->log('4 get rates');
74
+ if ( isset($shipment['rates_list']) ) {
75
+ if ( count($shipment['rates_list']) ) {
76
+ //Mage::log( $shipment['rates_list'] );
77
+ $handling = Mage::helper('buildateam_shippo')->getHandling();
78
+ foreach($shipment['rates_list'] as &$rate)
79
+ {
80
+ $rate['amount'] += $handling;
81
+ }
82
+
83
+
84
+ Mage::helper('buildateam_shippo')->log(count($shipment['rates_list']) . ' rates received');
85
+
86
+
87
+ return $shipment['rates_list'];
88
+ } else {
89
+ Mage::helper('buildateam_shippo')->log('ERROR: NO RATES !!!');
90
+ }
91
+ } else {
92
+ Mage::helper('buildateam_shippo')->log('ERROR: NO RATES !!!');
93
+ }
94
+
95
+
96
+ return null;
97
+ }
98
+
99
+ public function prepareFromAddress()
100
+ {
101
+ $regionId = Mage::helper('buildateam_shippo')->getFromRegion();
102
+ $region = Mage::getModel('directory/region')->load( $regionId );
103
+
104
+ $fromAddress = array(
105
+ 'object_purpose' => 'PURCHASE',
106
+ 'name' => Mage::helper('buildateam_shippo')->getFromName(),
107
+ 'company' => Mage::helper('buildateam_shippo')->getFromCompany(),
108
+ 'street1' => Mage::helper('buildateam_shippo')->getFromStreet(),
109
+ 'city' => Mage::helper('buildateam_shippo')->getFromCity(),
110
+ 'state' => $region->getCode(),
111
+ 'zip' => Mage::helper('buildateam_shippo')->getFromPostcode(),
112
+ 'country' => Mage::helper('buildateam_shippo')->getFromCountry(),
113
+ 'phone' => Mage::helper('buildateam_shippo')->getFromPhone(),
114
+ 'email' => Mage::helper('buildateam_shippo')->getFromEmail()
115
+ );
116
+
117
+ return $fromAddress;
118
+ }
119
+
120
+ public function prepareToAddress($mageShippingAddress)
121
+ {
122
+ $street = is_array($mageShippingAddress->getStreet()) ? current($mageShippingAddress->getStreet()) : $mageShippingAddress->getStreet();
123
+ $state = '';
124
+ if ( $regionId = $mageShippingAddress->getRegionId() )
125
+ {
126
+ $region = Mage::getModel('directory/region')->load( $regionId );
127
+ $state = $region->getCode();
128
+ }
129
+
130
+ $name = trim($mageShippingAddress->getFirstname() . ' ' . $mageShippingAddress->getLastname());
131
+ $name = empty($name) ? 'Customer' : $name;
132
+
133
+ $email = trim($mageShippingAddress->getEmail());
134
+ $email = empty($email) ? 'example@example.com' : $email;
135
+
136
+ // example fromAddress
137
+ $toAddress = array(
138
+ 'object_purpose' => 'PURCHASE',
139
+ 'name' => $name,
140
+ 'company' => $mageShippingAddress->getCompany(), # optional
141
+ 'street1' => $street,
142
+ 'city' => $mageShippingAddress->getCity(),
143
+ 'state' => $state,
144
+ 'zip' => $mageShippingAddress->getPostcode(),
145
+ 'country' => $mageShippingAddress->getCountryId(),
146
+ 'phone' => $mageShippingAddress->getTelephone(), # optional
147
+ 'email' => $email,
148
+ );
149
+
150
+ return $toAddress;
151
+ }
152
+
153
+ protected function _prepareParcel($items)
154
+ {
155
+ $parcel = new Buildateam_Shippo_Model_Parcel();
156
+ //$parcel->add(array(1, 5, 2), 1);
157
+ //$parcel->add(array(2, 4, 3), 2);
158
+ //$parcel->add(array(7, 1, 1), 1);
159
+
160
+ foreach ($items as $item) {
161
+ if ($item->getParentItem()) {
162
+ continue;
163
+ }
164
+ if ($item->getProduct()->isVirtual()) {
165
+ continue;
166
+ }
167
+
168
+ if ($item->getProduct()->getVisibility() != Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE) {
169
+ $product = Mage::getModel('catalog/product')->load( $item->getProduct()->getEntityId() );
170
+ $sDimensions = $product->getData('dimensions_lwh');
171
+
172
+ if ( $parcel->validateDimensionsString($sDimensions) ) {
173
+ $dimensions = $parcel->getDimensionsFromString($sDimensions);
174
+
175
+ Mage::helper('buildateam_shippo')->log('2 Parcel add');
176
+ Mage::helper('buildateam_shippo')->log('2 qty: ' . $item->getQty());
177
+ Mage::helper('buildateam_shippo')->log($dimensions);
178
+
179
+ $parcel->add($dimensions, $item->getQty());
180
+ }
181
+ }
182
+ }
183
+
184
+ return $parcel;
185
+ }
186
+
187
+ public function prepareParcel(Mage_Shipping_Model_Rate_Request $request)
188
+ {
189
+ $parcel = $this->_prepareParcel( $request->getAllItems() );
190
+
191
+ $parcel = array(
192
+ 'length' => $parcel->getLength(),
193
+ 'width' => $parcel->getWidth(),
194
+ 'height' => $parcel->getHeight(),
195
+ 'distance_unit' => Mage::helper('buildateam_shippo')->getSizeUnit(),
196
+ 'weight' => $request->getData('package_weight'),
197
+ 'mass_unit' => Mage::helper('buildateam_shippo')->getWeightUnit(),
198
+ );
199
+
200
+ Mage::helper('buildateam_shippo')->log('3 Parcel');
201
+ Mage::helper('buildateam_shippo')->log($parcel);
202
+
203
+ return $parcel;
204
+ }
205
+ }
app/code/community/Buildateam/Shippo/Model/Carrier.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Model_Carrier extends Mage_Shipping_Model_Carrier_Abstract implements Mage_Shipping_Model_Carrier_Interface
4
+ {
5
+ const CODE = 'buildateam_shippo'; // for static access
6
+
7
+ const EXPEDITED = 'expedited';
8
+ const OVERNIGHT = 'overnight';
9
+
10
+ protected $_code = self::CODE;
11
+
12
+ /**
13
+ * Returns allowed methods.
14
+ *
15
+ * @return array
16
+ */
17
+ public function getAllowedMethods()
18
+ {
19
+ $helper = Mage::helper('buildateam_shippo');
20
+ return array(
21
+ self::EXPEDITED => $helper->__('Expedited'),
22
+ self::OVERNIGHT => $helper->__('Overnight'),
23
+ );
24
+ }
25
+
26
+ /**
27
+ * We have no tracking.
28
+ *
29
+ * @return bool
30
+ */
31
+ public function isTrackingAvailable()
32
+ {
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * Collects shipping rates.
38
+ *
39
+ * @param Mage_Shipping_Model_Rate_Request $request
40
+ * @return bool|Mage_Shipping_Model_Rate_Result|null
41
+ */
42
+ public function collectRates(Mage_Shipping_Model_Rate_Request $request)
43
+ {
44
+ /** @var Mage_Shipping_Model_Rate_Result $result */
45
+ $result = Mage::getModel('shipping/rate_result');
46
+
47
+ try {
48
+ $aRates = Mage::helper('buildateam_shippo/shippo')->getRates($request);
49
+
50
+ if ( $aRates )
51
+ foreach($aRates as $aRate) {
52
+ $rate = $this->_prepareRate($aRate);
53
+ $result->append($rate);
54
+ }
55
+
56
+ } catch (Exception $e) {
57
+ Mage::helper('buildateam_shippo')->log( $e->getMessage() );
58
+ }
59
+
60
+ //$result->append($this->_getExpeditedRate());
61
+ //$result->append($this->_getOvernightRate());
62
+
63
+ return $result;
64
+ }
65
+
66
+ protected function _prepareRate($aRate)
67
+ {
68
+ $rate = Mage::getModel('shipping/rate_result_method');
69
+
70
+ $rate->setCarrier($this->_code);//($aRate['servicelevel_token']);
71
+ $rate->setCarrierTitle($aRate['provider']);
72
+ $rate->setMethod($aRate['servicelevel_token']);
73
+ $rate->setMethodTitle($aRate['servicelevel_name']);
74
+ $rate->setPrice($aRate['amount']);
75
+ $rate->setCost(0);
76
+
77
+ return $rate;
78
+ }
79
+
80
+ protected function _getExpeditedRate()
81
+ {
82
+ /** @var Mage_Shipping_Model_Rate_Result_Method $rate */
83
+ $rate = Mage::getModel('shipping/rate_result_method');
84
+
85
+ $rate->setCarrier($this->_code);
86
+ $rate->setCarrierTitle($this->getConfigData('title'));
87
+ $rate->setMethod(self::EXPEDITED);
88
+ $rate->setMethodTitle('Expedited');
89
+ $cost = $this->getDefaultHelper()->getExpeditedMethodCost();
90
+ $rate->setPrice($cost);
91
+ $rate->setCost(0);
92
+
93
+ return $rate;
94
+ }
95
+
96
+ protected function _getOvernightRate()
97
+ {
98
+ /** @var Mage_Shipping_Model_Rate_Result_Method $rate */
99
+ $rate = Mage::getModel('shipping/rate_result_method');
100
+
101
+ $rate->setCarrier($this->_code);
102
+ $rate->setCarrierTitle($this->getConfigData('title'));
103
+ $rate->setMethod(self::OVERNIGHT);
104
+ $rate->setMethodTitle('Overnight');
105
+ $cost = $this->getDefaultHelper()->getOvernightMethodCost();
106
+ $rate->setPrice($cost);
107
+ $rate->setCost(0);
108
+
109
+ return $rate;
110
+ }
111
+
112
+ /**
113
+ * Returns default helper.
114
+ *
115
+ * @return buildateam_shippo_Helper_Data
116
+ */
117
+ public function getDefaultHelper()
118
+ {
119
+ return Mage::helper('buildateam_shippo');
120
+ }
121
+ }
app/code/community/Buildateam/Shippo/Model/Observer.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+ class Buildateam_Shippo_Model_Observer
3
+ {
4
+
5
+ }
app/code/community/Buildateam/Shippo/Model/Parcel.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Model_Parcel extends Varien_Object
4
+ {
5
+ protected $_dimensions = array(0, 0, 0); // Height, Width, Length
6
+
7
+ public function add(array $dimensions, $qty)
8
+ {
9
+ if ( $this->validateDimensionsArray($dimensions) ) {
10
+ sort($dimensions); // 1 5 9
11
+ sort($this->_dimensions);
12
+
13
+ $this->_dimensions[2]= max($this->_dimensions[2], $dimensions[2]); // Length
14
+ $this->_dimensions[1]= max($this->_dimensions[1], $dimensions[1]); // Width
15
+ $this->_dimensions[0]= $this->_dimensions[0] + ($dimensions[0] * $qty); // Height
16
+
17
+ } else {
18
+ throw new Exception('Dimensions are invalid: ' . print_r($dimensions, 1));
19
+ }
20
+ }
21
+
22
+ public function validateDimensionsString($str)
23
+ {
24
+ $pattern = '/([\.\d])+x([\.\d])+x([\.\d])+/';
25
+ $result = preg_match($pattern, $str, $match);
26
+ if ( $match )
27
+ if ( count($match) ) {
28
+ $sDimensions = $match[0];
29
+ $aDimensions = explode('x', $sDimensions);
30
+
31
+ $isValid = $this->validateDimensionsArray($aDimensions);
32
+ return $isValid;
33
+ }
34
+
35
+ return false;
36
+ }
37
+
38
+ public function validateDimensionsArray(array $aDimensions)
39
+ {
40
+ if ( is_array($aDimensions) )
41
+ if ( count($aDimensions) == 3 )
42
+ if ( @array_walk($aDimensions, 'is_numeric') ) {
43
+
44
+ return true;
45
+ }
46
+
47
+ return false;
48
+ }
49
+
50
+ public function getDimensionsFromString($str)
51
+ {
52
+ $dimensions = explode('x', $str);
53
+ $dimensions = array_map('floatval', $dimensions);
54
+ return $dimensions;
55
+ }
56
+
57
+ public function getLength()
58
+ {
59
+ return $this->_dimensions[2];
60
+ }
61
+
62
+ public function getWidth()
63
+ {
64
+ return $this->_dimensions[1];
65
+ }
66
+
67
+ public function getHeight()
68
+ {
69
+ return $this->_dimensions[0];
70
+ }
71
+ }
app/code/community/Buildateam/Shippo/Model/System/Config/Source/Region.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Model_System_Config_Source_Region
4
+ extends Mage_Tax_Model_System_Config_Source_Tax_Region
5
+ {
6
+
7
+ }
app/code/community/Buildateam/Shippo/Model/System/Config/Source/Size.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Model_System_Config_Source_Size
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'in', 'label'=>Mage::helper('adminhtml')->__('Inches')),
9
+ array('value' => 'cm', 'label'=>Mage::helper('adminhtml')->__('Centimeters')),
10
+ );
11
+ }
12
+
13
+ public function toArray()
14
+ {
15
+ return array(
16
+ 'in' => Mage::helper('adminhtml')->__('Inches'),
17
+ 'cm' => Mage::helper('adminhtml')->__('Centimeters'),
18
+ );
19
+ }
20
+
21
+ }
app/code/community/Buildateam/Shippo/Model/System/Config/Source/Weight.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_Model_System_Config_Source_Weight
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'lb', 'label'=>Mage::helper('adminhtml')->__('Pounds')),
9
+ array('value' => 'kg', 'label'=>Mage::helper('adminhtml')->__('Kilograms')),
10
+ );
11
+ }
12
+
13
+ public function toArray()
14
+ {
15
+ return array(
16
+ 'lb' => Mage::helper('adminhtml')->__('Pounds'),
17
+ 'kg' => Mage::helper('adminhtml')->__('Kilograms'),
18
+ );
19
+ }
20
+
21
+ }
app/code/community/Buildateam/Shippo/controllers/TestController.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Buildateam_Shippo_TestController extends Mage_Core_Controller_Front_Action
4
+ {
5
+ public function indexAction()
6
+ {
7
+ echo '<br/>';
8
+ var_dump( Mage::helper('buildateam_shippo')->getPrivateAuthToken() );
9
+ echo '<br/>';
10
+ var_dump( Mage::helper('buildateam_shippo')->getFromName() );
11
+ echo '<br/>';
12
+ var_dump( Mage::helper('buildateam_shippo')->getFromCompany() );
13
+ echo '<br/>';
14
+ var_dump( Mage::helper('buildateam_shippo')->getFromCountry() );
15
+ echo '<br/>';
16
+ var_dump( Mage::helper('buildateam_shippo')->getFromRegion() );
17
+ echo '<br/>';
18
+ var_dump( Mage::helper('buildateam_shippo')->getFromPostcode() );
19
+ echo '<br/>';
20
+ var_dump( Mage::helper('buildateam_shippo')->getFromCity() );
21
+ echo '<br/>';
22
+ var_dump( Mage::helper('buildateam_shippo')->getFromStreet() );
23
+ echo '<br/>';
24
+ var_dump( Mage::helper('buildateam_shippo')->getFromPhone() );
25
+ echo '<br/>';
26
+ var_dump( Mage::helper('buildateam_shippo')->getFromEmail() );
27
+
28
+ echo '<br/>';
29
+ var_dump( Mage::helper('buildateam_shippo')->getWeightUnit() );
30
+ echo '<br/>';
31
+ var_dump( Mage::helper('buildateam_shippo')->getSizeUnit() );
32
+ echo '<br/>';
33
+ var_dump( Mage::helper('buildateam_shippo')->getHandling() );
34
+
35
+
36
+ }
37
+
38
+ }
app/code/community/Buildateam/Shippo/etc/adminhtml.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <system>
8
+ <children>
9
+ <config>
10
+ <children>
11
+ <buildateam_shippo translate="title" module="buildateam_shippo">
12
+ <title>Buildateam Shippo</title>
13
+ <sort_order>0</sort_order>
14
+ </buildateam_shippo>
15
+ </children>
16
+ </config>
17
+ </children>
18
+ </system>
19
+ </children>
20
+ </admin>
21
+ </resources>
22
+ </acl>
23
+ </config>
app/code/community/Buildateam/Shippo/etc/config.xml ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Buildateam_Shippo>
5
+ <version>1.0.2</version>
6
+ </Buildateam_Shippo>
7
+ </modules>
8
+ <default>
9
+ <carriers>
10
+ <buildateam_shippo>
11
+ <active>0</active>
12
+
13
+ <private_auth_token>167263307a16e8b1c51fbf4b055decc85b683678</private_auth_token>
14
+
15
+ <from_name>JOHN SMITH</from_name>
16
+ <from_company>BEST COMPANY EVER</from_company>
17
+ <from_country>US</from_country>
18
+ <from_region>19</from_region>
19
+ <from_postcode>30320</from_postcode>
20
+ <from_city>Atlanta</from_city>
21
+ <from_street>9050A ALEXA AVE SE</from_street>
22
+ <from_phone>111-111-1111</from_phone>
23
+ <from_email>john.smith@bestcompany.com</from_email>
24
+
25
+ <weight_unit>lb</weight_unit>
26
+ <size_unit>in</size_unit>
27
+ <shipping_handling>0</shipping_handling>
28
+
29
+ <model>buildateam_shippo/carrier</model>
30
+ <title>Shippo</title>
31
+ <sort_order>10</sort_order>
32
+ <sallowspecific>1</sallowspecific>
33
+ <specificcountry>US</specificcountry>
34
+ <economy_description>
35
+ <![CDATA[<div class="s_methodTop">(3-6 business days)</div><div class="s_methodBottom">ORDER BY 1PM PST DEC 15TH TO RECIEVE BY DEC 24TH</div>]]></economy_description>
36
+ <expedited_description>
37
+ <![CDATA[<div class="s_methodTop">(2-3 business days)</div><div class="s_methodBottom">ORDER BY 10AM PST DEC 22ND TO RECIEVE BY DEC 24TH</div>]]></expedited_description>
38
+ <overnight_description>
39
+ <![CDATA[<div class="s_methodTop">(1-2 business days)</div><div class="s_methodMiddle">ORDERS MUST PLACED BY 10AM PST M-TH</div><div class="s_methodBottom">ORDER BY 1PM PST DEC 23RD TO RECIEVE BY DEC 24TH</div>]]></overnight_description>
40
+ <expedited_cost>32.00</expedited_cost>
41
+ <overnight_cost>70.00</overnight_cost>
42
+ </buildateam_shippo>
43
+ </carriers>
44
+ <buildateam_shippo>
45
+ <general>
46
+ <email_delay>30</email_delay>
47
+ <enable>1</enable>
48
+ </general>
49
+ </buildateam_shippo>
50
+ </default>
51
+ <frontend>
52
+ <layout>
53
+ <updates>
54
+ </updates>
55
+ </layout>
56
+ <routers>
57
+ <buildateam_shippo>
58
+ <use>standard</use>
59
+ <args>
60
+ <module>Buildateam_Shippo</module>
61
+ <frontName>buildateamshippo</frontName>
62
+ </args>
63
+ </buildateam_shippo>
64
+ </routers>
65
+ </frontend>
66
+ <adminhtml>
67
+ <layout>
68
+ <updates>
69
+ <buildateam_shippo>
70
+ <file>buildateam/shippo.xml</file>
71
+ </buildateam_shippo>
72
+ </updates>
73
+ </layout>
74
+ </adminhtml>
75
+ <global>
76
+ <helpers>
77
+ <buildateam_shippo>
78
+ <class>Buildateam_Shippo_Helper</class>
79
+ </buildateam_shippo>
80
+ </helpers>
81
+ <blocks>
82
+ <buildateam_shippo>
83
+ <class>Buildateam_Shippo_Block</class>
84
+ </buildateam_shippo>
85
+ </blocks>
86
+ <models>
87
+ <buildateam_shippo>
88
+ <class>Buildateam_Shippo_Model</class>
89
+ <resourceModel>buildateam_shippo_resource</resourceModel>
90
+ </buildateam_shippo>
91
+ <buildateam_shippo_resource>
92
+ <class>Buildateam_Shippo_Model_Resource</class>
93
+ <entities>
94
+ </entities>
95
+ </buildateam_shippo_resource>
96
+ </models>
97
+ <resources>
98
+ <buildateam_shippo_setup>
99
+ <setup>
100
+ <module>Buildateam_Shippo</module>
101
+ </setup>
102
+ <connection>
103
+ <use>core_setup</use>
104
+ </connection>
105
+ </buildateam_shippo_setup>
106
+ </resources>
107
+ <template>
108
+ <email>
109
+ </email>
110
+ </template>
111
+ <events>
112
+ </events>
113
+ </global>
114
+ <crontab>
115
+ <jobs>
116
+ </jobs>
117
+ </crontab>
118
+ </config>
app/code/community/Buildateam/Shippo/etc/system.xml ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <carriers>
5
+ <groups>
6
+ <buildateam_shippo translate="label" module="buildateam_shippo">
7
+ <label>Shippo</label>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>10</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <fields>
14
+ <active translate="label">
15
+ <label>Enabled for Checkout</label>
16
+ <frontend_type>select</frontend_type>
17
+ <source_model>adminhtml/system_config_source_yesno</source_model>
18
+ <sort_order>2</sort_order>
19
+ <show_in_default>1</show_in_default>
20
+ <show_in_website>1</show_in_website>
21
+ <show_in_store>0</show_in_store>
22
+ </active>
23
+
24
+ <private_auth_token translate="label">
25
+ <label>Private Auth Token</label>
26
+ <frontend_type>text</frontend_type>
27
+ <validate>required-entry</validate>
28
+ <sort_order>8</sort_order>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>1</show_in_store>
32
+ </private_auth_token>
33
+
34
+ <from_name translate="label">
35
+ <label>From Name</label>
36
+ <frontend_type>text</frontend_type>
37
+ <validate>required-entry</validate>
38
+ <sort_order>9</sort_order>
39
+ <show_in_default>1</show_in_default>
40
+ <show_in_website>1</show_in_website>
41
+ <show_in_store>1</show_in_store>
42
+ </from_name>
43
+ <from_company translate="label">
44
+ <label>From Company</label>
45
+ <frontend_type>text</frontend_type>
46
+ <validate>required-entry</validate>
47
+ <sort_order>10</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ </from_company>
52
+ <from_country translate="label">
53
+ <label>From Country</label>
54
+ <frontend_type>select</frontend_type>
55
+ <validate>required-entry</validate>
56
+ <source_model>adminhtml/system_config_source_country</source_model>
57
+ <sort_order>11</sort_order>
58
+ <show_in_default>1</show_in_default>
59
+ <show_in_website>1</show_in_website>
60
+ <show_in_store>1</show_in_store>
61
+ </from_country>
62
+ <from_region translate="label">
63
+ <label><![CDATA[From State/Province]]></label>
64
+ <frontend_type>select</frontend_type>
65
+ <validate>required-entry</validate>
66
+ <frontend_model>buildateam_shippo/adminhtml_frontend_region_updater</frontend_model>
67
+ <source_model>buildateam_shippo/system_config_source_region</source_model>
68
+ <sort_order>12</sort_order>
69
+ <show_in_default>1</show_in_default>
70
+ <show_in_website>1</show_in_website>
71
+ <show_in_store>1</show_in_store>
72
+ </from_region>
73
+ <from_postcode translate="label">
74
+ <label><![CDATA[From Zip/Postal Code]]></label>
75
+ <frontend_type>text</frontend_type>
76
+ <validate>required-entry</validate>
77
+ <sort_order>13</sort_order>
78
+ <show_in_default>1</show_in_default>
79
+ <show_in_website>1</show_in_website>
80
+ <show_in_store>1</show_in_store>
81
+ </from_postcode>
82
+ <from_city translate="label">
83
+ <label>From City</label>
84
+ <frontend_type>text</frontend_type>
85
+ <validate>required-entry</validate>
86
+ <sort_order>14</sort_order>
87
+ <show_in_default>1</show_in_default>
88
+ <show_in_website>1</show_in_website>
89
+ <show_in_store>1</show_in_store>
90
+ </from_city>
91
+ <from_street translate="label">
92
+ <label>From Street Address</label>
93
+ <frontend_type>text</frontend_type>
94
+ <validate>required-entry</validate>
95
+ <sort_order>15</sort_order>
96
+ <show_in_default>1</show_in_default>
97
+ <show_in_website>1</show_in_website>
98
+ <show_in_store>1</show_in_store>
99
+ </from_street>
100
+ <from_phone translate="label">
101
+ <label>From Telephone</label>
102
+ <frontend_type>text</frontend_type>
103
+ <validate>required-entry</validate>
104
+ <sort_order>16</sort_order>
105
+ <show_in_default>1</show_in_default>
106
+ <show_in_website>1</show_in_website>
107
+ <show_in_store>1</show_in_store>
108
+ </from_phone>
109
+ <from_email translate="label">
110
+ <label>From Email</label>
111
+ <frontend_type>text</frontend_type>
112
+ <validate>required-entry</validate>
113
+ <sort_order>17</sort_order>
114
+ <show_in_default>1</show_in_default>
115
+ <show_in_website>1</show_in_website>
116
+ <show_in_store>1</show_in_store>
117
+ </from_email>
118
+
119
+ <weight_unit translate="label">
120
+ <label>Weight Unit</label>
121
+ <frontend_type>select</frontend_type>
122
+ <validate>required-entry</validate>
123
+ <source_model>buildateam_shippo/system_config_source_weight</source_model>
124
+ <sort_order>19</sort_order>
125
+ <show_in_default>1</show_in_default>
126
+ <show_in_website>1</show_in_website>
127
+ <show_in_store>1</show_in_store>
128
+ </weight_unit>
129
+ <size_unit translate="label">
130
+ <label>Dimensions Unit</label>
131
+ <frontend_type>select</frontend_type>
132
+ <validate>required-entry</validate>
133
+ <source_model>buildateam_shippo/system_config_source_size</source_model>
134
+ <sort_order>21</sort_order>
135
+ <show_in_default>1</show_in_default>
136
+ <show_in_website>1</show_in_website>
137
+ <show_in_store>1</show_in_store>
138
+ </size_unit>
139
+ <shipping_handling translate="label">
140
+ <label><![CDATA[Shipping & Handling]]></label>
141
+ <comment>In store currency.</comment>
142
+ <frontend_type>text</frontend_type>
143
+ <validate>required-entry validate-number</validate>
144
+ <sort_order>23</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>1</show_in_store>
148
+ </shipping_handling>
149
+
150
+ <sallowspecific translate="label">
151
+ <label>Ship to Applicable Countries</label>
152
+ <frontend_type>select</frontend_type>
153
+ <sort_order>70</sort_order>
154
+ <frontend_class>shipping-applicable-country</frontend_class>
155
+ <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
156
+ <show_in_default>1</show_in_default>
157
+ <show_in_website>1</show_in_website>
158
+ <show_in_store>0</show_in_store>
159
+ </sallowspecific>
160
+ <specificcountry translate="label">
161
+ <label>Ship to Specific Countries</label>
162
+ <frontend_type>multiselect</frontend_type>
163
+ <sort_order>80</sort_order>
164
+ <source_model>adminhtml/system_config_source_country</source_model>
165
+ <show_in_default>1</show_in_default>
166
+ <show_in_website>1</show_in_website>
167
+ <show_in_store>0</show_in_store>
168
+ <can_be_empty>1</can_be_empty>
169
+ </specificcountry>
170
+ <!--
171
+ <sort_order translate="label">
172
+ <label>Sort Order</label>
173
+ <frontend_type>text</frontend_type>
174
+ <sort_order>90</sort_order>
175
+ <show_in_default>1</show_in_default>
176
+ <show_in_website>1</show_in_website>
177
+ <show_in_store>0</show_in_store>
178
+ </sort_order>
179
+ -->
180
+ </fields>
181
+ </buildateam_shippo>
182
+ </groups>
183
+ </carriers>
184
+ </sections>
185
+ </config>
app/code/community/Buildateam/Shippo/sql/buildateam_shippo_setup/mysql4-upgrade-1.0.0-1.0.1.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+ $installer = $this;
3
+
4
+ $installer->startSetup();
5
+
6
+
7
+ $installer->endSetup();
app/code/community/Buildateam/Shippo/sql/buildateam_shippo_setup/mysql4-upgrade-1.0.1-1.0.2.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $objCatalogEavSetup = Mage::getResourceModel('catalog/eav_mysql4_setup', 'core_setup');
8
+
9
+ $objCatalogEavSetup->addAttribute(
10
+ "catalog_product", // Entity the new attribute is supposed to be added to
11
+ "dimensions_lwh", // attribute code
12
+ array(
13
+ "type" => "varchar",
14
+ "label" => "Dimensions",
15
+ "note" => "Length, Width and Height. Example: 15.5x7.5x5",
16
+ "input" => "text",
17
+ "frontend_class" => "validate-dimentions-lwh",
18
+ "global" => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
19
+ // Dont know if this is really necessary, but it makes sure
20
+ // the attribute is created as a system attribute:
21
+ 'required' => false,
22
+ "user_defined" => false,
23
+ //"apply_to" => Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE,
24
+ 'apply_to' => implode(',', array(
25
+ Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE,
26
+ Mage_Catalog_Model_Product_Type::TYPE_SIMPLE,
27
+ )),
28
+ 'sort_order' => 9
29
+ )
30
+ );
31
+
32
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/buildateam/shippo.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <adminhtml_catalog_product_new>
4
+ <update handle="buildateam_shippo_catalog_product_js"/>
5
+ </adminhtml_catalog_product_new>
6
+
7
+ <adminhtml_catalog_product_edit>
8
+ <update handle="buildateam_shippo_catalog_product_js"/>
9
+ </adminhtml_catalog_product_edit>
10
+
11
+ <buildateam_shippo_catalog_product_js>
12
+ <reference name="before_body_end">
13
+ <block type="core/template" name="buildateam_shippo_product_js" as="buildateam_shippo_product_js" template="buildateam/shippo/catalog/product/js.phtml"/>
14
+ </reference>
15
+ </buildateam_shippo_catalog_product_js>
16
+ </layout>
app/design/adminhtml/default/default/template/buildateam/shippo/catalog/product/js.phtml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ ?>
4
+ <script type="text/javascript">
5
+ Event.observe(window, 'load', function(){
6
+ Validation.add('validate-dimentions-lwh','Should be in the form of 17.3x8x7',function(v){
7
+
8
+ if ( Validation.get('IsEmpty').test(v) || v.match(/([\.\d])+x([\.\d])+x([\.\d])+/) ) {
9
+ return true;
10
+ }
11
+
12
+ return false;
13
+ });
14
+ });
15
+ </script>
app/etc/modules/Buildateam_Shippo.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Buildateam_Shippo>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ <version>1.0.0</version>
8
+ </Buildateam_Shippo>
9
+ </modules>
10
+ </config>
lib/Shippo.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Tested on PHP 5.2, 5.3
4
+ if (!function_exists('curl_init')) {
5
+ throw new Exception('Shippo needs the CURL PHP extension.');
6
+ }
7
+ if (!function_exists('json_decode')) {
8
+ throw new Exception('Shippo needs the JSON PHP extension.');
9
+ }
10
+ if (!function_exists('mb_detect_encoding')) {
11
+ throw new Exception('Shippo needs the Multibyte String PHP extension.');
12
+ }
13
+
14
+ // Shippo singleton
15
+ require(dirname(__FILE__) . '/Shippo/Shippo.php');
16
+
17
+ // Utilities
18
+ require(dirname(__FILE__) . '/Shippo/Util.php');
19
+ require(dirname(__FILE__) . '/Shippo/Util/Set.php');
20
+
21
+ // Errors
22
+ require(dirname(__FILE__) . '/Shippo/Error.php');
23
+ require(dirname(__FILE__) . '/Shippo/ApiError.php');
24
+ require(dirname(__FILE__) . '/Shippo/ApiConnectionError.php');
25
+ require(dirname(__FILE__) . '/Shippo/AuthenticationError.php');
26
+ require(dirname(__FILE__) . '/Shippo/InvalidRequestError.php');
27
+ require(dirname(__FILE__) . '/Shippo/RateLimitError.php');
28
+
29
+ // Plumbing
30
+ require(dirname(__FILE__) . '/Shippo/Object.php');
31
+ require(dirname(__FILE__) . '/Shippo/ApiRequestor.php');
32
+ require(dirname(__FILE__) . '/Shippo/ApiResource.php');
33
+ require(dirname(__FILE__) . '/Shippo/SingletonApiResource.php');
34
+ require(dirname(__FILE__) . '/Shippo/AttachedObject.php');
35
+ require(dirname(__FILE__) . '/Shippo/List.php');
36
+
37
+ // Shippo API Resources
38
+ require(dirname(__FILE__) . '/Shippo/Address.php');
39
+ require(dirname(__FILE__) . '/Shippo/Parcel.php');
40
+ require(dirname(__FILE__) . '/Shippo/Shipment.php');
41
+ require(dirname(__FILE__) . '/Shippo/Rate.php');
42
+ require(dirname(__FILE__) . '/Shippo/Transaction.php');
43
+ require(dirname(__FILE__) . '/Shippo/CustomsItem.php');
44
+ require(dirname(__FILE__) . '/Shippo/CustomsDeclaration.php');
45
+ require(dirname(__FILE__) . '/Shippo/Refund.php');
46
+ require(dirname(__FILE__) . '/Shippo/Manifest.php');
47
+ require(dirname(__FILE__) . '/Shippo/CarrierAccount.php');
lib/Shippo/Address.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Address extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param string $class Ignored.
7
+ *
8
+ * @return string The class URL for this resource. It needs to be special
9
+ * cased because it doesn't fit into the standard resource pattern.
10
+ * The standard resource pattern is name + s, e.g. parcel becomes parcels.
11
+ */
12
+ public static function classUrl($class)
13
+ {
14
+ return "/v1/addresses";
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ * @param string|null $apiKey
20
+ *
21
+ * @return Shippo_Adress Create an Address.
22
+ */
23
+ public static function create($params = null, $apiKey = null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ /**
30
+ * @param array|null $params
31
+ *
32
+ * @return Shippo_Retrieve Get an address.
33
+ */
34
+ public static function retrieve($id, $apiKey = null)
35
+ {
36
+ $class = get_class();
37
+ return self::_scopedRetrieve($class, $id, $apiKey);
38
+ }
39
+
40
+ /**
41
+ * @param array|null $params
42
+ *
43
+ * @return Shippo_All Get all the addresses.
44
+ */
45
+ public static function all($params = null, $apiKey = null)
46
+ {
47
+ $class = get_class();
48
+ return self::_scopedAll($class, $params, $apiKey);
49
+ }
50
+
51
+ /**
52
+ * @param array|null $params
53
+ *
54
+ * @return Shippo_Validate Validate an address.
55
+ */
56
+ public static function validate($id)
57
+ {
58
+ $class = get_class();
59
+ return self::_scopedValidate($class, $id);
60
+ }
61
+ }
lib/Shippo/ApiConnectionError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_ApiConnectionError extends Shippo_Error
4
+ {
5
+ }
lib/Shippo/ApiError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_ApiError extends Shippo_Error
4
+ {
5
+ }
lib/Shippo/ApiRequestor.php ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Shippo_ApiRequestor
3
+ {
4
+ /**
5
+ * @var string $apiKey The API key that's to be used to make requests.
6
+ */
7
+ public $apiKey;
8
+
9
+ public function __construct($apiKey = null)
10
+ {
11
+ $this->_apiKey = $apiKey;
12
+ }
13
+
14
+ /**
15
+ * @param string $url The path to the API endpoint.
16
+ *
17
+ * @returns string The full path.
18
+ */
19
+ public static function apiUrl($url = '')
20
+ {
21
+ $apiBase = Shippo::$apiBase;
22
+ return "$apiBase$url";
23
+ }
24
+
25
+ /**
26
+ * @param string|mixed $value A string to UTF8-encode.
27
+ *
28
+ * @returns string|mixed The UTF8-encoded string, or the object passed in if
29
+ * it wasn't a string.
30
+ */
31
+ public static function utf8($value)
32
+ {
33
+ if (is_string($value) && mb_detect_encoding($value, "UTF-8", TRUE) != "UTF-8") {
34
+ return utf8_encode($value);
35
+ } else {
36
+ return $value;
37
+ }
38
+ }
39
+
40
+ private static function _encodeObjects($d)
41
+ {
42
+ if ($d instanceof Shippo_ApiResource) {
43
+ return self::utf8($d->id);
44
+ } else if ($d instanceof Shippo_Object) {
45
+ return self::utf8($d->object_id);
46
+ } else if ($d === true) {
47
+ return 'true';
48
+ } else if ($d === false) {
49
+ return 'false';
50
+ } else if (is_array($d)) {
51
+ $res = array();
52
+ foreach ($d as $k => $v)
53
+ $res[$k] = self::_encodeObjects($v);
54
+ return $res;
55
+ } else {
56
+ return self::utf8($d);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @param array $arr An map of param keys to values.
62
+ * @param string|null $prefix (It doesn't look like we ever use $prefix...)
63
+ *
64
+ * @returns string A querystring, essentially.
65
+ */
66
+ public static function encode($arr, $prefix = null)
67
+ {
68
+ if (!is_array($arr))
69
+ return $arr;
70
+
71
+ $r = array();
72
+ foreach ($arr as $k => $v) {
73
+ if (is_null($v))
74
+ continue;
75
+
76
+ if ($prefix && $k && !is_int($k))
77
+ $k = $prefix . "[" . $k . "]";
78
+ else if ($prefix)
79
+ $k = $prefix . "[]";
80
+
81
+ if (is_array($v)) {
82
+ $r[] = self::encode($v, $k, true);
83
+ } else {
84
+ $r[] = urlencode($k) . "=" . urlencode($v);
85
+ }
86
+ }
87
+
88
+ return implode("&", $r);
89
+ }
90
+
91
+ /**
92
+ * @param string $method
93
+ * @param string $url
94
+ * @param array|null $params
95
+ *
96
+ * @return array An array whose first element is the response and second
97
+ * element is the API key used to make the request.
98
+ */
99
+ public function request($method, $url, $params = null)
100
+ {
101
+ if (!$params)
102
+ $params = array();
103
+ list($rbody, $rcode, $myApiKey) = $this->_requestRaw($method, $url, $params);
104
+ $resp = $this->_interpretResponse($rbody, $rcode);
105
+ return array(
106
+ $resp,
107
+ $myApiKey
108
+ );
109
+ }
110
+
111
+
112
+ /**
113
+ * @param string $rbody A JSON string.
114
+ * @param int $rcode
115
+ * @param array $resp
116
+ *
117
+ * @throws Shippo_InvalidRequestError if the error is caused by the user.
118
+ * @throws Shippo_AuthenticationError if the error is caused by a lack of
119
+ * permissions.
120
+ * @throws Shippo_ApiError otherwise.
121
+ */
122
+ public function handleApiError($rbody, $rcode, $resp)
123
+ {
124
+ // Array is not currently being returned by API, making the below N/A
125
+ // if (!is_array($resp) || !isset($resp['error'])) {
126
+ // $msg = "Invalid response object from API: $rbody "
127
+ // ."(HTTP response code was $rcode)";
128
+ // throw new Shippo_ApiError($msg, $rcode, $rbody, $resp);
129
+ // }
130
+
131
+ $msg = "message not set";
132
+ $param = "parameters not set";
133
+ $code = "code not set";
134
+
135
+ // Temporary setting of msg to rbody
136
+ $msg = $rbody;
137
+
138
+ // Parameters necessary for error code construction are not provided
139
+ // $error = $resp['error'];
140
+ // $msg = isset($error['message']) ? $error['message'] : null;
141
+ // $param = isset($error['param']) ? $error['param'] : null;
142
+ // $code = isset($error['code']) ? $error['code'] : null;
143
+
144
+ switch ($rcode) {
145
+ case 400:
146
+ throw new Shippo_InvalidRequestError($msg, $param, $rcode, $rbody, $resp);
147
+ case 404:
148
+ throw new Shippo_InvalidRequestError($msg, $param, $rcode, $rbody, $resp);
149
+ case 401:
150
+ throw new Shippo_AuthenticationError($msg, $rcode, $rbody, $resp);
151
+ default:
152
+ throw new Shippo_ApiError($msg, $rcode, $rbody, $resp);
153
+ }
154
+ }
155
+
156
+ private function _requestRaw($method, $url, $params)
157
+ {
158
+ $myApiKey = $this->_apiKey;
159
+ if (!$myApiKey)
160
+ $myApiKey = Shippo::$apiKey;
161
+
162
+ if (!$myApiKey) {
163
+ $msg = 'No credentials provided.';
164
+ throw new Shippo_AuthenticationError($msg);
165
+ }
166
+
167
+ $absUrl = $this->apiUrl($url);
168
+ $params = self::_encodeObjects($params);
169
+ $langVersion = phpversion();
170
+ $uname = php_uname();
171
+ $headers = array(
172
+ 'Content-Type: application/json',
173
+ 'Authorization: ShippoToken ' . $myApiKey,
174
+ 'Accept: application/json',
175
+ 'User-Agent: Shippo/v1 PHPBindings/' . Shippo::VERSION
176
+ );
177
+
178
+ list($rbody, $rcode) = $this->_curlRequest($method, $absUrl, $headers, $params);
179
+ return array(
180
+ $rbody,
181
+ $rcode,
182
+ $myApiKey
183
+ );
184
+ }
185
+
186
+ private function _interpretResponse($rbody, $rcode)
187
+ {
188
+ try {
189
+ $resp = json_decode($rbody, true);
190
+ }
191
+ catch (Exception $e) {
192
+ $msg = "Invalid response body from API: $rbody " . "(HTTP response code was $rcode)";
193
+ throw new Shippo_ApiError($msg, $rcode, $rbody);
194
+ }
195
+
196
+ if ($rcode < 200 || $rcode >= 300) {
197
+ $this->handleApiError($rbody, $rcode, $resp);
198
+ }
199
+ return $resp;
200
+ }
201
+
202
+ private function _curlRequest($method, $absUrl, $headers, $params)
203
+ {
204
+ $curl = curl_init();
205
+ $method = strtolower($method);
206
+ $curlOptions = array();
207
+
208
+ // Request Method
209
+ if ($method == 'get') {
210
+ $curlOptions[CURLOPT_HTTPGET] = 1;
211
+ if (count($params) > 0) {
212
+ $encoded = self::encode($params);
213
+ $absUrl = "$absUrl?$encoded";
214
+ }
215
+ } else if ($method == 'post') {
216
+ $curlOptions[CURLOPT_POST] = 1;
217
+ $curlOptions[CURLOPT_POSTFIELDS] = json_encode($params);
218
+ } else if ($method == 'put') {
219
+ $curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT';
220
+ $curlOptions[CURLOPT_POSTFIELDS] = json_encode($params);
221
+ } else {
222
+ throw new Error("Unrecognized method {$method}");
223
+ }
224
+ // echo("<br><br>ABSOLUTE URL: " . $absUrl . "<br><br>");
225
+ $absUrl = self::utf8($absUrl);
226
+ $curlOptions[CURLOPT_URL] = $absUrl;
227
+ $curlOptions[CURLOPT_RETURNTRANSFER] = true;
228
+ $curlOptions[CURLOPT_CONNECTTIMEOUT] = 30;
229
+ $curlOptions[CURLOPT_TIMEOUT] = 80;
230
+ $curlOptions[CURLOPT_HTTPHEADER] = $headers;
231
+ // echo(var_dump($headers));
232
+ $curlOptions[CURLOPT_FOLLOWLOCATION] = TRUE;
233
+
234
+ curl_setopt_array($curl, $curlOptions);
235
+ $httpBody = curl_exec($curl);
236
+
237
+ $errorNum = curl_errno($curl);
238
+ if ($errorNum == CURLE_SSL_CACERT || $errorNum == CURLE_SSL_PEER_CERTIFICATE || $errorNum == 77) {
239
+ curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/../cacert.pem');
240
+ $httpBody = curl_exec($curl);
241
+ }
242
+
243
+ if ($httpBody === false) {
244
+ $errorNum = curl_errno($curl);
245
+ $message = curl_error($curl);
246
+ curl_close($curl);
247
+ $this->handleCurlError($errorNum, $message);
248
+ }
249
+
250
+ $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
251
+ curl_close($curl);
252
+ // echo("<br><br>RESPONSE<br><br>".$httpBody);
253
+ // echo("<br><br>STATUS<br><br>".$httpStatus);
254
+ return array(
255
+ $httpBody,
256
+ $httpStatus
257
+ );
258
+ }
259
+
260
+ /**
261
+ * @param number $errno
262
+ * @param string $message
263
+ * @throws Shippo_ApiConnectionError
264
+ */
265
+ public function handleCurlError($errno, $message)
266
+ {
267
+ $apiBase = Shippo::$apiBase;
268
+ switch ($errno) {
269
+ case CURLE_COULDNT_CONNECT:
270
+ case CURLE_COULDNT_RESOLVE_HOST:
271
+ case CURLE_OPERATION_TIMEOUTED:
272
+ $msg = "Could not connect to Shippo ($apiBase).";
273
+ break;
274
+ default:
275
+ $msg = "Unexpected error communicating with Shippo. " . "If this problem persists,";
276
+ }
277
+ $msg .= " let us know by contacting us through our contact form.";
278
+ $msg .= "\n\n(Network error [errno $errno]: $message)";
279
+ throw new Shippo_ApiConnectionError($msg);
280
+ }
281
+ }
lib/Shippo/ApiResource.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Shippo_ApiResource extends Shippo_Object
4
+ {
5
+ protected static function _scopedRetrieve($class, $id, $apiKey = null)
6
+ {
7
+ $instance = new $class($id, $apiKey);
8
+ $instance->refresh();
9
+ return $instance;
10
+ }
11
+
12
+ /**
13
+ * @returns Shippo_ApiResource The refreshed resource.
14
+ */
15
+ public function refresh()
16
+ {
17
+ $requestor = new Shippo_ApiRequestor($this->_apiKey);
18
+ $url = $this->instanceUrl();
19
+
20
+ list($response, $apiKey) = $requestor->request('get', $url, $this->_retrieveOptions);
21
+ $this->refreshFrom($response, $apiKey);
22
+ return $this;
23
+ }
24
+
25
+ /**
26
+ * @param string $class
27
+ *
28
+ * @returns string The name of the class, with namespacing and underscores
29
+ * stripped.
30
+ */
31
+ public static function className($class)
32
+ {
33
+ // Useful for namespaces: Foo\Shippo_Shipment
34
+ if ($postfixNamespaces = strrchr($class, '\\')) {
35
+ $class = substr($postfixNamespaces, 1);
36
+ }
37
+ // Useful for underscored 'namespaces': Foo_Shippo_Shipment
38
+ if ($postfixFakeNamespaces = strrchr($class, 'Shippo_')) {
39
+ $class = $postfixFakeNamespaces;
40
+ }
41
+ if (substr($class, 0, strlen('Shippo')) == 'Shippo') {
42
+ $class = substr($class, strlen('Shippo'));
43
+ }
44
+ $class = str_replace('_', '', $class);
45
+ $name = urlencode($class);
46
+ $name = strtolower($name);
47
+ return $name;
48
+ }
49
+
50
+ /**
51
+ * @param string $class
52
+ *
53
+ * @returns string The endpoint URL for the given class.
54
+ */
55
+ public static function classUrl($class)
56
+ {
57
+ $base = self::_scopedLsb($class, 'className', $class);
58
+ return "/v1/${base}s";
59
+ }
60
+
61
+ /**
62
+ * @returns string The full API URL for this API resource.
63
+ */
64
+ public function instanceUrl()
65
+ {
66
+ $id = $this['id'];
67
+ $class = get_class($this);
68
+ if ($id === null) {
69
+ $message = "Could not determine which URL to request: " . "$class instance has invalid ID: $id";
70
+ throw new Shippo_InvalidRequestError($message, null);
71
+ }
72
+ $id = Shippo_ApiRequestor::utf8($id);
73
+ $base = $this->_lsb('classUrl', $class);
74
+ $extn = urlencode($id);
75
+ return "$base/$extn";
76
+ }
77
+
78
+ private static function _validateCall($method, $params = null, $apiKey = null)
79
+ {
80
+ if ($params && !is_array($params)) {
81
+ $message = "You must pass an array as the first argument to Shippo API ";
82
+ throw new Shippo_Error($message);
83
+ }
84
+
85
+ if ($apiKey && !is_string($apiKey)) {
86
+ $message = 'The second argument to Shippo API method calls is an ' . 'optional per-request apiKey (credentials base_64 encoded), which must be a string';
87
+ throw new Shippo_Error($message);
88
+ }
89
+ }
90
+
91
+ protected static function _scopedAll($class, $params = null, $apiKey = null)
92
+ {
93
+ self::_validateCall('all', $params, $apiKey);
94
+ $requestor = new Shippo_ApiRequestor($apiKey);
95
+ $url = self::_scopedLsb($class, 'classUrl', $class);
96
+ list($response, $apiKey) = $requestor->request('get', $url, $params);
97
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
98
+ }
99
+
100
+ protected static function _scopedCreate($class, $params = null, $apiKey = null)
101
+ {
102
+ self::_validateCall('create', $params, $apiKey);
103
+ $requestor = new Shippo_ApiRequestor($apiKey);
104
+ $url = self::_scopedLsb($class, 'classUrl', $class);
105
+ // Correction for malformed shippo URLs
106
+ $url = $url . "/";
107
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
108
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
109
+ }
110
+
111
+ protected static function _scopedUpdate($class, $id, $params = null, $apiKey = null)
112
+ {
113
+ self::_validateCall('update', $params, $apiKey);
114
+ $requestor = new Shippo_ApiRequestor($apiKey);
115
+ $url = self::_scopedLsb($class, 'classUrl', $class) . "/" . $id;
116
+ list($response, $apiKey) = $requestor->request('put', $url, $params);
117
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
118
+ }
119
+
120
+ // Special Case for Rates which has parameters of the format: /url/parameter/url
121
+ protected static function _scopedGet($class, $id, $params = null, $apiKey = null)
122
+ {
123
+ self::_validateCall('create', $params, $apiKey);
124
+ $requestor = new Shippo_ApiRequestor($apiKey);
125
+ $url = "/v1/shipments/" . $id . "/rates/";
126
+ list($response, $apiKey) = $requestor->request('get', $url, $params);
127
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
128
+ }
129
+
130
+ // Special case for Address Validation
131
+ protected static function _scopedValidate($class, $id, $params = null, $apiKey = null)
132
+ {
133
+ self::_validateCall('create', $params, $apiKey);
134
+ $requestor = new Shippo_ApiRequestor($apiKey);
135
+ $url = self::_scopedLsb($class, 'classUrl', $class) . "/" . $id . "/validate/";
136
+ list($response, $apiKey) = $requestor->request('get', $url, $params);
137
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
138
+ }
139
+ }
lib/Shippo/AttachedObject.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // e.g. metadata on Shippo objects.
4
+ class Shippo_AttachedObject extends Shippo_Object
5
+ {
6
+ /**
7
+ * Updates this object.
8
+ *
9
+ * @param array $properties A mapping of properties to update on this object.
10
+ */
11
+ public function replaceWith($properties)
12
+ {
13
+ $removed = array_diff(array_keys($this->_values), array_keys($properties));
14
+ // Don't unset, but rather set to null so we send up '' for deletion.
15
+ foreach ($removed as $k) {
16
+ $this->$k = null;
17
+ }
18
+
19
+ foreach ($properties as $k => $v) {
20
+ $this->$k = $v;
21
+ }
22
+ }
23
+ }
lib/Shippo/AuthenticationError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_AuthenticationError extends Shippo_Error
4
+ {
5
+ }
lib/Shippo/CarrierAccount.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_CarrierAccount extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param string $class Ignored.
7
+ *
8
+ * @return string The class URL for this resource. It needs to be special
9
+ * cased because it doesn't fit into the standard resource pattern.
10
+ * The standard resource pattern is name + s, e.g. parcel becomes parcels.
11
+ */
12
+ public static function classUrl($class)
13
+ {
14
+ return "/v1/carrier_accounts";
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ * @param string|null $apiKey
20
+ *
21
+ * @return Shippo_CustomsItem Create a customs item.
22
+ */
23
+ public static function create($params = null, $apiKey = null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ /**
30
+ * @param array|null $params
31
+ *
32
+ * @return Shippo_Retrieve Get a customs item.
33
+ */
34
+ public static function retrieve($id, $apiKey = null)
35
+ {
36
+ $class = get_class();
37
+ return self::_scopedRetrieve($class, $id, $apiKey);
38
+ }
39
+
40
+ /**
41
+ * @param array|null $params
42
+ *
43
+ * @return Shippo_All Get all the customs items.
44
+ */
45
+ public static function all($params = null, $apiKey = null)
46
+ {
47
+ $class = get_class();
48
+ return self::_scopedAll($class, $params, $apiKey);
49
+ }
50
+
51
+ /**
52
+ * @param id of the CarrierAccount to be updated
53
+ *
54
+ * @return Shippo_Retrieve Get a customs item.
55
+ */
56
+ public static function update($id, $params, $apiKey = null)
57
+ {
58
+ $class = get_class();
59
+ return self::_scopedUpdate($class, $id, $params, $apiKey);
60
+ }
61
+
62
+ }
lib/Shippo/CustomsDeclaration.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_CustomsDeclaration extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param string $class Ignored.
7
+ *
8
+ * @return string The class URL for this resource. It needs to be special
9
+ * cased because it doesn't fit into the standard resource pattern.
10
+ * The standard resource pattern is name + s, e.g. parcel becomes parcels.
11
+ */
12
+ public static function classUrl($class)
13
+ {
14
+ return "/v1/customs/declarations";
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ * @param string|null $apiKey
20
+ *
21
+ * @return Shippo_CustomsDeclaration Create a customs declaration.
22
+ */
23
+ public static function create($params = null, $apiKey = null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ /**
30
+ * @param array|null $params
31
+ *
32
+ * @return Shippo_Retrieve Get a customs declaration.
33
+ */
34
+ public static function retrieve($id, $apiKey = null)
35
+ {
36
+ $class = get_class();
37
+ return self::_scopedRetrieve($class, $id, $apiKey);
38
+ }
39
+
40
+ /**
41
+ * @param array|null $params
42
+ *
43
+ * @return Shippo_All Get all the customs declarations.
44
+ */
45
+ public static function all($params = null, $apiKey = null)
46
+ {
47
+ $class = get_class();
48
+ return self::_scopedAll($class, $params, $apiKey);
49
+ }
50
+ }
lib/Shippo/CustomsItem.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_CustomsItem extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param string $class Ignored.
7
+ *
8
+ * @return string The class URL for this resource. It needs to be special
9
+ * cased because it doesn't fit into the standard resource pattern.
10
+ * The standard resource pattern is name + s, e.g. parcel becomes parcels.
11
+ */
12
+ public static function classUrl($class)
13
+ {
14
+ return "/v1/customs/items";
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ * @param string|null $apiKey
20
+ *
21
+ * @return Shippo_CustomsItem Create a customs item.
22
+ */
23
+ public static function create($params = null, $apiKey = null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ /**
30
+ * @param array|null $params
31
+ *
32
+ * @return Shippo_Retrieve Get a customs item.
33
+ */
34
+ public static function retrieve($id, $apiKey = null)
35
+ {
36
+ $class = get_class();
37
+ return self::_scopedRetrieve($class, $id, $apiKey);
38
+ }
39
+
40
+ /**
41
+ * @param array|null $params
42
+ *
43
+ * @return Shippo_All Get all the customs items.
44
+ */
45
+ public static function all($params = null, $apiKey = null)
46
+ {
47
+ $class = get_class();
48
+ return self::_scopedAll($class, $params, $apiKey);
49
+ }
50
+ }
lib/Shippo/Error.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Error extends Exception
4
+ {
5
+ public function __construct($message, $httpStatus = null, $httpBody = null, $jsonBody = null)
6
+ {
7
+ parent::__construct($message);
8
+ $this->httpStatus = $httpStatus;
9
+ $this->httpBody = $httpBody;
10
+ $this->jsonBody = $jsonBody;
11
+ }
12
+
13
+ public function getHttpStatus()
14
+ {
15
+ return $this->httpStatus;
16
+ }
17
+
18
+ public function getHttpBody()
19
+ {
20
+ return $this->httpBody;
21
+ }
22
+
23
+ public function getJsonBody()
24
+ {
25
+ return $this->jsonBody;
26
+ }
27
+ }
lib/Shippo/InvalidRequestError.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_InvalidRequestError extends Shippo_Error
4
+ {
5
+ public function __construct($message, $param, $httpStatus = null, $httpBody = null, $jsonBody = null)
6
+ {
7
+ parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
8
+ $this->param = $param;
9
+ }
10
+ }
lib/Shippo/List.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_List extends Shippo_Object
4
+ {
5
+ public function all($params = null)
6
+ {
7
+ $requestor = new Shippo_ApiRequestor($this->_apiKey);
8
+ list($response, $apiKey) = $requestor->request('get', $this['url'], $params);
9
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
10
+ }
11
+
12
+ public function create($params = null)
13
+ {
14
+ $requestor = new Shippo_ApiRequestor($this->_apiKey);
15
+ list($response, $apiKey) = $requestor->request('post', $this['url'], $params);
16
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
17
+ }
18
+
19
+ public function retrieve($id, $params = null)
20
+ {
21
+ $requestor = new Shippo_ApiRequestor($this->_apiKey);
22
+ $base = $this['url'];
23
+ $id = Shippo_ApiRequestor::utf8($id);
24
+ $extn = urlencode($id);
25
+ list($response, $apiKey) = $requestor->request('get', "$base/$extn", $params);
26
+ return Shippo_Util::convertToShippoObject($response, $apiKey);
27
+ }
28
+
29
+ }
lib/Shippo/Manifest.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Manifest extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ * @param string|null $apiKey
8
+ *
9
+ * @return Shippo_Manifest Create a manifest.
10
+ */
11
+ public static function create($params = null, $apiKey = null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ *
20
+ * @return Shippo_Retrieve Get a manifest.
21
+ */
22
+ public static function retrieve($id, $apiKey = null)
23
+ {
24
+ $class = get_class();
25
+ return self::_scopedRetrieve($class, $id, $apiKey);
26
+ }
27
+
28
+ /**
29
+ * @param array|null $params
30
+ *
31
+ * @return Shippo_All Get all the manifests.
32
+ */
33
+ public static function all($params = null, $apiKey = null)
34
+ {
35
+ $class = get_class();
36
+ return self::_scopedAll($class, $params, $apiKey);
37
+ }
38
+ }
lib/Shippo/Object.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Object implements ArrayAccess
4
+ {
5
+ /**
6
+ * @var Shippo_Util_Set Attributes that should not be sent to the API because
7
+ * they're not updatable (e.g. API key, ID).
8
+ */
9
+ public static $permanentAttributes;
10
+ /**
11
+ * @var Shippo_Util_Set Attributes that are nested but still updatable from
12
+ * the parent class's URL (e.g. metadata).
13
+ */
14
+ public static $nestedUpdatableAttributes;
15
+
16
+ public static function init()
17
+ {
18
+ self::$permanentAttributes = new Shippo_Util_Set(array(
19
+ '_apiKey',
20
+ 'id'
21
+ ));
22
+ self::$nestedUpdatableAttributes = new Shippo_Util_Set(array(
23
+ 'metadata'
24
+ ));
25
+ }
26
+
27
+ protected $_apiKey;
28
+ protected $_values;
29
+ protected $_unsavedValues;
30
+ protected $_transientValues;
31
+ protected $_retrieveOptions;
32
+
33
+ public function __construct($id = null, $apiKey = null)
34
+ {
35
+ $this->_apiKey = $apiKey;
36
+ $this->_values = array();
37
+ $this->_unsavedValues = new Shippo_Util_Set();
38
+ $this->_transientValues = new Shippo_Util_Set();
39
+
40
+ $this->_retrieveOptions = array();
41
+ if (is_array($id)) {
42
+ foreach ($id as $key => $value) {
43
+ if ($key != 'id') {
44
+ $this->_retrieveOptions[$key] = $value;
45
+ }
46
+ }
47
+ $id = $id['id'];
48
+ }
49
+
50
+ if ($id !== null) {
51
+ $this->id = $id;
52
+ }
53
+ }
54
+
55
+ // Standard accessor magic methods
56
+ public function __set($k, $v)
57
+ {
58
+ if ($v === "") {
59
+ throw new InvalidArgumentException('You cannot set \'' . $k . '\'to an empty string. ' . 'We interpret empty strings as NULL in requests. ' . 'You may set obj->' . $k . ' = NULL to delete the property');
60
+ }
61
+
62
+ if (self::$nestedUpdatableAttributes->includes($k) && isset($this->$k) && is_array($v)) {
63
+ $this->$k->replaceWith($v);
64
+ } else {
65
+ // TODO: may want to clear from $_transientValues (Won't be user-visible).
66
+ $this->_values[$k] = $v;
67
+ }
68
+ if (!self::$permanentAttributes->includes($k))
69
+ $this->_unsavedValues->add($k);
70
+ }
71
+ public function __isset($k)
72
+ {
73
+ return isset($this->_values[$k]);
74
+ }
75
+ public function __unset($k)
76
+ {
77
+ unset($this->_values[$k]);
78
+ $this->_transientValues->add($k);
79
+ $this->_unsavedValues->discard($k);
80
+ }
81
+ public function __get($k)
82
+ {
83
+ if (array_key_exists($k, $this->_values)) {
84
+ return $this->_values[$k];
85
+ } else if ($this->_transientValues->includes($k)) {
86
+ $class = get_class($this);
87
+ $attrs = join(', ', array_keys($this->_values));
88
+ $message = "Shippo Notice: Undefined property of $class instance: $k. " . "HINT: The $k attribute was set in the past, however. " . "It was then wiped when refreshing the object " . "with the result returned by Shippo's API, " . "probably as a result of a save(). The attributes currently " . "available on this object are: $attrs";
89
+ error_log($message);
90
+ return null;
91
+ } else {
92
+ $class = get_class($this);
93
+ error_log("Shippo Notice: Undefined property of $class instance: $k");
94
+ return null;
95
+ }
96
+ }
97
+
98
+ // ArrayAccess methods
99
+ public function offsetSet($k, $v)
100
+ {
101
+ $this->$k = $v;
102
+ }
103
+
104
+ public function offsetExists($k)
105
+ {
106
+ return array_key_exists($k, $this->_values);
107
+ }
108
+
109
+ public function offsetUnset($k)
110
+ {
111
+ unset($this->$k);
112
+ }
113
+ public function offsetGet($k)
114
+ {
115
+ return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
116
+ }
117
+
118
+ public function keys()
119
+ {
120
+ return array_keys($this->_values);
121
+ }
122
+
123
+ /**
124
+ * This unfortunately needs to be public to be used in Util.php
125
+ *
126
+ * @param string $class
127
+ * @param array $values
128
+ * @param string|null $apiKey
129
+ *
130
+ * @return Shippo_Object The object constructed from the given values.
131
+ */
132
+ public static function scopedConstructFrom($class, $values, $apiKey = null)
133
+ {
134
+ $obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
135
+ $obj->refreshFrom($values, $apiKey);
136
+ return $obj;
137
+ }
138
+
139
+ /**
140
+ * @param array $values
141
+ * @param string|null $apiKey
142
+ *
143
+ * @return Shippo_Object The object of the same class as $this constructed
144
+ * from the given values.
145
+ */
146
+ public static function constructFrom($values, $apiKey = null)
147
+ {
148
+ return self::scopedConstructFrom(__CLASS__, $values, $apiKey);
149
+ }
150
+
151
+ /**
152
+ * Refreshes this object using the provided values.
153
+ *
154
+ * @param array $values
155
+ * @param string $apiKey
156
+ * @param boolean $partial Defaults to false.
157
+ */
158
+ public function refreshFrom($values, $apiKey, $partial = false)
159
+ {
160
+ $this->_apiKey = $apiKey;
161
+
162
+ // Wipe old state before setting new. This is useful for e.g. updating a
163
+ // customer, where there is no persistent card parameter. Mark those values
164
+ // which don't persist as transient
165
+ if ($partial) {
166
+ $removed = new Shippo_Util_Set();
167
+ } else {
168
+ $removed = array_diff(array_keys($this->_values), array_keys($values));
169
+ }
170
+
171
+ foreach ($removed as $k) {
172
+ if (self::$permanentAttributes->includes($k))
173
+ continue;
174
+ unset($this->$k);
175
+ }
176
+
177
+ foreach ($values as $k => $v) {
178
+ if (self::$permanentAttributes->includes($k) && isset($this[$k]))
179
+ continue;
180
+
181
+ if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
182
+ $this->_values[$k] = Shippo_Object::scopedConstructFrom('Shippo_AttachedObject', $v, $apiKey);
183
+ } else {
184
+ $this->_values[$k] = Shippo_Util::convertToShippoObject($v, $apiKey);
185
+ }
186
+
187
+ $this->_transientValues->discard($k);
188
+ $this->_unsavedValues->discard($k);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * @return array A recursive mapping of attributes to values for this object,
194
+ * including the proper value for deleted attributes.
195
+ */
196
+ public function serializeParameters()
197
+ {
198
+ $params = array();
199
+ if ($this->_unsavedValues) {
200
+ foreach ($this->_unsavedValues->toArray() as $k) {
201
+ $v = $this->$k;
202
+ if ($v === NULL) {
203
+ $v = '';
204
+ }
205
+ $params[$k] = $v;
206
+ }
207
+ }
208
+
209
+ // Get nested updates.
210
+ foreach (self::$nestedUpdatableAttributes->toArray() as $property) {
211
+ if (isset($this->$property) && $this->$property instanceOf Shippo_Object) {
212
+ $params[$property] = $this->$property->serializeParameters();
213
+ }
214
+ }
215
+ return $params;
216
+ }
217
+
218
+ // Pretend to have late static bindings, even in PHP 5.2
219
+ protected function _lsb($method)
220
+ {
221
+ $class = get_class($this);
222
+ $args = array_slice(func_get_args(), 1);
223
+ return call_user_func_array(array(
224
+ $class,
225
+ $method
226
+ ), $args);
227
+ }
228
+ protected static function _scopedLsb($class, $method)
229
+ {
230
+ $args = array_slice(func_get_args(), 2);
231
+ return call_user_func_array(array(
232
+ $class,
233
+ $method
234
+ ), $args);
235
+ }
236
+
237
+ public function __toJSON()
238
+ {
239
+ if (defined('JSON_PRETTY_PRINT')) {
240
+ return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
241
+ } else {
242
+ return json_encode($this->__toArray(true));
243
+ }
244
+ }
245
+
246
+ public function __toString()
247
+ {
248
+ return $this->__toJSON();
249
+ }
250
+
251
+ public function __toArray($recursive = false)
252
+ {
253
+ if ($recursive) {
254
+ return Shippo_Util::convertShippoObjectToArray($this->_values);
255
+ } else {
256
+ return $this->_values;
257
+ }
258
+ }
259
+ }
260
+
261
+
262
+ Shippo_Object::init();
lib/Shippo/Parcel.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Parcel extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ * @param string|null $apiKey
8
+ *
9
+ * @return Shippo_Parcel Create a Parcel.
10
+ */
11
+ public static function create($params = null, $apiKey = null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ *
20
+ * @return Shippo_Retrieve Get a Parcel.
21
+ */
22
+ public static function retrieve($id, $apiKey = null)
23
+ {
24
+ $class = get_class();
25
+ return self::_scopedRetrieve($class, $id, $apiKey);
26
+ }
27
+
28
+ /**
29
+ * @param array|null $params
30
+ *
31
+ * @return Shippo_All Get all the Parcels.
32
+ */
33
+ public static function all($params = null, $apiKey = null)
34
+ {
35
+ $class = get_class();
36
+ return self::_scopedAll($class, $params, $apiKey);
37
+ }
38
+ }
lib/Shippo/Rate.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Rate extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ *
8
+ * @return Shippo_Retrieve Get a Rate.
9
+ */
10
+ public static function retrieve($id, $apiKey = null)
11
+ {
12
+ $class = get_class();
13
+ return self::_scopedRetrieve($class, $id, $apiKey);
14
+ }
15
+
16
+ /**
17
+ * @param array|null $params
18
+ *
19
+ * @return Shippo_All Get all the Rates.
20
+ */
21
+ public static function all($params = null, $apiKey = null)
22
+ {
23
+ $class = get_class();
24
+ return self::_scopedAll($class, $params, $apiKey);
25
+ }
26
+ }
lib/Shippo/RateLimitError.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_RateLimitError extends Shippo_InvalidRequestError
4
+ {
5
+ public function __construct($message, $param, $httpStatus = null, $httpBody = null, $jsonBody = null)
6
+ {
7
+ parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
8
+ }
9
+ }
lib/Shippo/Refund.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Refund extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ * @param string|null $apiKey
8
+ *
9
+ * @return Shippo_Refund Create a Refund.
10
+ */
11
+ public static function create($params = null, $apiKey = null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ *
20
+ * @return Shippo_Retrieve Get a Refund.
21
+ */
22
+ public static function retrieve($id, $apiKey = null)
23
+ {
24
+ $class = get_class();
25
+ return self::_scopedRetrieve($class, $id, $apiKey);
26
+ }
27
+
28
+ /**
29
+ * @param array|null $params
30
+ *
31
+ * @return Shippo_All Get all the Refunds.
32
+ */
33
+ public static function all($params = null, $apiKey = null)
34
+ {
35
+ $class = get_class();
36
+ return self::_scopedAll($class, $params, $apiKey);
37
+ }
38
+ }
lib/Shippo/Shipment.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Shipment extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ * @param string|null $apiKey
8
+ *
9
+ * @return Shippo_Shipment Create a Shipment.
10
+ */
11
+ public static function create($params = null, $apiKey = null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ *
20
+ * @return Shippo_Retrieve Get a Shipment.
21
+ */
22
+ public static function retrieve($id, $apiKey = null)
23
+ {
24
+ $class = get_class();
25
+ return self::_scopedRetrieve($class, $id, $apiKey);
26
+ }
27
+
28
+ /**
29
+ * @param array|null $params
30
+ *
31
+ * @return Shippo_All Get all the Shipments.
32
+ */
33
+ public static function all($params = null, $apiKey = null)
34
+ {
35
+ $class = get_class();
36
+ return self::_scopedAll($class, $params, $apiKey);
37
+ }
38
+
39
+ /**
40
+ * @param array|null $params
41
+ *
42
+ * @return Shippo_Get_Shipping_Rates Get the rates for a Shipment.
43
+ */
44
+ public static function get_shipping_rates($params = null, $apiKey = null)
45
+ {
46
+ $class = get_class();
47
+ $id = $params['id'];
48
+ return self::_scopedGet($class, $id, $params = null, $apiKey = null);
49
+ }
50
+ }
lib/Shippo/Shippo.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Shippo
4
+ {
5
+ /**
6
+ * @var string The Shippo API key to be used for requests.
7
+ */
8
+ public static $apiKey;
9
+ /**
10
+ * @var string The base URL for the Shippo API.
11
+ */
12
+ public static $apiBase = 'https://api.goshippo.com';
13
+ /**
14
+ * @var string|null The version of the Shippo API to use for requests.
15
+ */
16
+ public static $apiVersion = null;
17
+ /**
18
+ * @var boolean Defaults to true.
19
+ */
20
+ public static $verifySslCerts = false;
21
+ const VERSION = '0.0.1';
22
+
23
+ /**
24
+ * @return string The API key used for requests.
25
+ */
26
+ public static function getApiKey()
27
+ {
28
+ return self::$apiKey;
29
+ }
30
+
31
+ /**
32
+ * Sets the API key to be used for requests.
33
+ *
34
+ * @param string $apiKey
35
+ */
36
+ public static function setApiKey($apiKey)
37
+ {
38
+ self::$apiKey = $apiKey;
39
+ }
40
+
41
+ /**
42
+ * @return string The API version used for requests. null if we're using the
43
+ * latest version.
44
+ */
45
+ public static function getApiVersion()
46
+ {
47
+ return self::$apiVersion;
48
+ }
49
+
50
+ /**
51
+ * @param string $apiVersion The API version to use for requests.
52
+ */
53
+ public static function setApiVersion($apiVersion)
54
+ {
55
+ self::$apiVersion = $apiVersion;
56
+ }
57
+ }
lib/Shippo/SingletonApiResource.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Shippo_SingletonApiResource extends Shippo_ApiResource
4
+ {
5
+ protected static function _scopedSingletonRetrieve($class, $apiKey = null)
6
+ {
7
+ $instance = new $class(null, $apiKey);
8
+ $instance->refresh();
9
+ return $instance;
10
+ }
11
+
12
+ /**
13
+ * @param Shippo_SingletonApiResource $class
14
+ * @return string The endpoint associated with this singleton class.
15
+ */
16
+ public static function classUrl($class)
17
+ {
18
+ $base = self::className($class);
19
+ return "/v1/${base}";
20
+ }
21
+
22
+ /**
23
+ * @return string The endpoint associated with this singleton API resource.
24
+ */
25
+ public function instanceUrl()
26
+ {
27
+ $class = get_class($this);
28
+ $base = self::classUrl($class);
29
+ return "$base";
30
+ }
31
+ }
lib/Shippo/Transaction.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Transaction extends Shippo_ApiResource
4
+ {
5
+ /**
6
+ * @param array|null $params
7
+ * @param string|null $apiKey
8
+ *
9
+ * @return Shippo_Transaction Create a Transaction.
10
+ */
11
+ public static function create($params = null, $apiKey = null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ /**
18
+ * @param array|null $params
19
+ *
20
+ * @return Shippo_Retrieve Get a Transaction.
21
+ */
22
+ public static function retrieve($id, $apiKey = null)
23
+ {
24
+ $class = get_class();
25
+ return self::_scopedRetrieve($class, $id, $apiKey);
26
+ }
27
+
28
+ /**
29
+ * @param array|null $params
30
+ *
31
+ * @return Shippo_All Get all the Transactions.
32
+ */
33
+ public static function all($params = null, $apiKey = null)
34
+ {
35
+ $class = get_class();
36
+ return self::_scopedAll($class, $params, $apiKey);
37
+ }
38
+ }
lib/Shippo/Util.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Shippo_Util
4
+ {
5
+ /**
6
+ * Whether the provided array (or other) is a list rather than a dictionary.
7
+ *
8
+ * @param array|mixed $array
9
+ * @return boolean True if the given object is a list.
10
+ */
11
+ public static function isList($array)
12
+ {
13
+ if (!is_array($array))
14
+ return false;
15
+
16
+ // TODO: generally incorrect, but it's correct given Shippo's response
17
+ foreach (array_keys($array) as $k) {
18
+ if (!is_numeric($k))
19
+ return false;
20
+ }
21
+ return true;
22
+ }
23
+
24
+ /**
25
+ * Recursively converts the PHP Shippo object to an array.
26
+ *
27
+ * @param array $values The PHP Shippo object to convert.
28
+ * @return array
29
+ */
30
+ public static function convertShippoObjectToArray($values)
31
+ {
32
+ $results = array();
33
+ foreach ($values as $k => $v) {
34
+ // FIXME: this is an encapsulation violation
35
+ if ($k[0] == '_') {
36
+ continue;
37
+ }
38
+ if ($v instanceof Shippo_Object) {
39
+ $results[$k] = $v->__toArray(true);
40
+ } else if (is_array($v)) {
41
+ $results[$k] = self::convertShippoObjectToArray($v);
42
+ } else {
43
+ $results[$k] = $v;
44
+ }
45
+ }
46
+ return $results;
47
+ }
48
+
49
+ /**
50
+ * Converts a response from the Shippo API to the corresponding PHP object.
51
+ *
52
+ * @param array $resp The response from the Shippo API.
53
+ * @param string $apiKey
54
+ * @return Shippo_Object|array
55
+ */
56
+ public static function convertToShippoObject($resp, $apiKey)
57
+ {
58
+ // TODO: Have API Return Object: Type in order to cast properly
59
+ $types = array(
60
+ 'QUOTE' => 'Shippo_Address'
61
+ );
62
+ if (self::isList($resp)) {
63
+ $mapped = array();
64
+ foreach ($resp as $i)
65
+ array_push($mapped, self::convertToShippoObject($i, $apiKey));
66
+ return $mapped;
67
+ } else if (is_array($resp)) {
68
+ if (isset($resp['object_purpose']) && is_string($resp['object_purpose']) && isset($types[$resp['object_purpose']])) {
69
+ $class = $types[$resp['object_purpose']];
70
+ } else {
71
+ $class = 'Shippo_Object';
72
+ }
73
+ return Shippo_Object::scopedConstructFrom($class, $resp, $apiKey);
74
+ } else {
75
+ return $resp;
76
+ }
77
+ }
78
+ }
lib/Shippo/Util/Set.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shippo_Util_Set
4
+ {
5
+ private $_elts;
6
+
7
+ public function __construct($members = array())
8
+ {
9
+ $this->_elts = array();
10
+ foreach ($members as $item)
11
+ $this->_elts[$item] = true;
12
+ }
13
+
14
+ public function includes($elt)
15
+ {
16
+ return isset($this->_elts[$elt]);
17
+ }
18
+
19
+ public function add($elt)
20
+ {
21
+ $this->_elts[$elt] = true;
22
+ }
23
+
24
+ public function discard($elt)
25
+ {
26
+ unset($this->_elts[$elt]);
27
+ }
28
+
29
+ // TODO: make Set support foreach
30
+ public function toArray()
31
+ {
32
+ return array_keys($this->_elts);
33
+ }
34
+ }
package.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Buildateam_Shippo</name>
4
+ <version>1.0.2</version>
5
+ <stability>stable</stability>
6
+ <license uri="https://www.buildateam.io/end-user-license-agreement/">Buildateam EULA</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Shippo integration</summary>
10
+ <description>An ability to display Shippo rates on checkout</description>
11
+ <notes>Don't forget to specify Weight and Dimensions for each Product and to enable Shippo rates here:&#xD;
12
+ System &gt; Configuration &gt; Sales &gt; ShippingMethods &gt; Shippo</notes>
13
+ <authors><author><name>Buildateam</name><user>buildateam</user><email>lex.mustafin@gmail.com</email></author></authors>
14
+ <date>2016-06-25</date>
15
+ <time>09:25:09</time>
16
+ <contents><target name="magecommunity"><dir name="Buildateam"><dir name="Shippo"><dir name="Block"><dir name="Adminhtml"><dir name="Frontend"><dir name="Region"><file name="Updater.php" hash="c491ea63bb45159cbda060fdf34b28ed"/></dir></dir></dir></dir><dir name="Helper"><file name="Carrier.php" hash="f93af56bb9826876c268416f9184d5a7"/><file name="Data.php" hash="c3f6ff836f419117d72a9acebe49d44a"/><file name="Shippo.php" hash="3d29ac520a44b9285c2f5e21d047c143"/></dir><dir name="Model"><file name="Carrier.php" hash="40dd78de6c9bd294b211c6f91e6ed689"/><file name="Observer.php" hash="59c46326dccf9c81091c8ca52f68bc5e"/><file name="Parcel.php" hash="30c84b345500aa7cea172c7b2abe61a7"/><dir name="System"><dir name="Config"><dir name="Source"><file name="Region.php" hash="cdf4cbed7d6dceb603cd6401605bb10d"/><file name="Size.php" hash="0ea6eacc2955389c62fcfcdeeaa1fd14"/><file name="Weight.php" hash="3591a1ff37eae8ec84f8b92d5110efbb"/></dir></dir></dir></dir><dir name="controllers"><file name="TestController.php" hash="e19b9a65cd73204b32878d2abdba7b93"/></dir><dir name="etc"><file name="adminhtml.xml" hash="ba1a357269d7968d1e939c3feb7f8a61"/><file name="config.xml" hash="1a36080ffd2b55bcb0e2894e334bffdb"/><file name="system.xml" hash="f5562a9cf3bb7028d498b220d83eb2a7"/></dir><dir name="sql"><dir name="buildateam_shippo_setup"><file name="mysql4-upgrade-1.0.0-1.0.1.php" hash="04e9efc84ad102faf3194e1a5c718e93"/><file name="mysql4-upgrade-1.0.1-1.0.2.php" hash="38cff0661faa7e326b672ce9be98ae3d"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="buildateam"><file name="shippo.xml" hash="649b5e41b8f0c5ba89489092b072e35b"/></dir></dir><dir name="template"><dir name="buildateam"><dir name="shippo"><dir name="catalog"><dir name="product"><file name="js.phtml" hash="fcc991ee690d1d69aed347bd8e8ff7e4"/></dir></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Buildateam_Shippo.xml" hash="803295b3c8b0d76e0a41fc91deffa015"/></dir></target><target name="mage"><dir name="lib"><dir name="Shippo"><file name="Address.php" hash="0208872b93ba60dde1791f5f67fc7979"/><file name="ApiConnectionError.php" hash="53b5d4a25910750a114999dbb35f4324"/><file name="ApiError.php" hash="5c5e5e113708e24ca10f937dc1f9a5c3"/><file name="ApiRequestor.php" hash="7328a1116ffbf42f0fce6397e6b9271a"/><file name="ApiResource.php" hash="7f92abf3a9499626230c3a036dd40caf"/><file name="AttachedObject.php" hash="98a0779cdb695f2f3dcb32ee2af608b4"/><file name="AuthenticationError.php" hash="745de030343d577e54d349b718270448"/><file name="CarrierAccount.php" hash="dac1ce095ae6a895fbb07e29a9a5834a"/><file name="CustomsDeclaration.php" hash="5c699342e6449c2f800cfe24889c0776"/><file name="CustomsItem.php" hash="25f617bcf964cab836c5d751e1a0fb3f"/><file name="Error.php" hash="d1fc506089f558c7dc48c5ca793d9390"/><file name="InvalidRequestError.php" hash="04204981812de2f60a5d2a2c74cbc581"/><file name="List.php" hash="54858f45588b6ffd4de4557ad45bd118"/><file name="Manifest.php" hash="a417ae0e066e6888a5448d853429ebb0"/><file name="Object.php" hash="28dfb631fb002d85ecff4a7aa6e39199"/><file name="Parcel.php" hash="89019233291f552999a8fc5200f5a030"/><file name="Rate.php" hash="191acfd8220a17a312770074e37a0feb"/><file name="RateLimitError.php" hash="b5665ac78b534e2bba675ba51bf98f2e"/><file name="Refund.php" hash="dad1adad037cde3002b85e02c0f54dc1"/><file name="Shipment.php" hash="faacbd7f16487fc8b14d605f1a9045dc"/><file name="Shippo.php" hash="7d75f1e7bcfb8d7dde994c1052406d2a"/><file name="SingletonApiResource.php" hash="a3a43a218712be59b769b7e7374e4097"/><file name="Transaction.php" hash="353f22ce5fec45737cf111b8609b6d98"/><dir name="Util"><file name="Set.php" hash="6ba0be5f0ac9429fa7e015297bbee99f"/></dir><file name="Util.php" hash="4246ec80cdea8da4505f83946ff8ab4d"/></dir><file name="Shippo.php" hash="fe9f59c6610bf16b1b925921f72f92ae"/></dir></target></contents>
17
+ <compatible/>
18
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
19
+ </package>