├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── config └── config.php ├── phpunit.xml ├── src ├── Helpers │ ├── Endpoint.php │ ├── Orders.php │ └── Shipments.php ├── Models │ ├── Address.php │ ├── AdvancedOptions.php │ ├── CustomsItem.php │ ├── Dimensions.php │ ├── InsuranceOptions.php │ ├── InternationalOptions.php │ ├── ItemOption.php │ ├── Order.php │ ├── OrderItem.php │ ├── Product.php │ ├── ProductCategory.php │ ├── ProductTag.php │ ├── Webhook.php │ └── Weight.php ├── ShipStation.php └── ShipStationServiceProvider.php └── tests └── ShipStationTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.4 4 | before_script: composer install 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Joe Campo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LaravelShipStation 2 | =============== 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE.md) 5 | [![Latest Version on Packagist][packagist-downloads]][link-packagist] 6 | [![Build Status](https://travis-ci.org/joecampo/laravel-shipstation.svg?branch=master)](https://travis-ci.org/joecampo/laravel-shipstation) 7 | 8 | This is a simple PHP API wrapper for [ShipStation](http://shipstation.com) built for Laravel. 9 | 10 | Installation 11 | ------------ 12 | This package can be installed via [Composer](http://getcomposer.org) by requiring the ```campo/laravel-shipstation``` package in your project's ```composer.json``` 13 | ```json 14 | { 15 | "require": { 16 | "campo/laravel-shipstation": "^5.0" 17 | } 18 | } 19 | ``` 20 | 21 | Then at your Laravel project root run: 22 | ```sh 23 | composer update 24 | ``` 25 | 26 | Second, add the LaravelShipStation service provider to your providers array located in ```config/app.php``` 27 | ```php 28 | LaravelShipStation\ShipStationServiceProvider::class 29 | ``` 30 | 31 | After installing via composer you will need to publish the configuration: 32 | ```php 33 | php artisan vendor:publish 34 | ``` 35 | This will create the configuration file for your API key and API secret key at ```config/shipstation.php```. You will need to obtain your API & Secret key from ShipStation: [How can I get access to ShipStation's API?](https://help.shipstation.com/hc/en-us/articles/206638917-How-can-I-get-access-to-ShipStation-s-API-) 36 | 37 | If ShipStation has provided you with a partner API key, set it in your configuration file. 38 | ## Dependencies 39 | LaravelShipStation uses ```GuzzleHttp\Guzzle``` 40 | ## Endpoints 41 | Endpoints for the API are accessed via properties (e.g. ```$shipStation->orders->get($options)``` will make a GET request to ```/orders/{$options}```). The default endpoint is /orders/. Valid endpoints include: 42 | * accounts 43 | * carriers 44 | * customers 45 | * fulfillments 46 | * orders 47 | * products 48 | * shipments 49 | * stores 50 | * users 51 | * warehouses 52 | * webhooks 53 | 54 | ## Methods 55 | ### GET 56 | ```php 57 | $shipStation->{$endpoint}->get($options = [], $endpoint = ''); 58 | ``` 59 | Example of getting an order with the order id (ShipStation's internal order id) of 123 & Getting ShipStations internal order id by the order number. 60 | ```php 61 | $shipStation = $this->app['LaravelShipStation\ShipStation']; 62 | 63 | // Fetch an order by orderId == 123, orderId is defined by ShipStation 64 | $order = $shipStation->orders->get([], $endpoint = 123); // returns \stdClass 65 | 66 | // Fetch an orderId by the orderNumber, which may be user defined 67 | $order = $shipStation->orders->getOrderId('ORD-789'); // returns integer 68 | ```` 69 | ### POST 70 | ```php 71 | $shipStation->{$endpoint}->post($options = [], $endpoint = ''); 72 | ``` 73 | The second parameter ($endpoint) is for any additional endpoints that need to be added. For example, to create an order the POST request would go to /orders/createorder. "createorder" is the additional endpoint since we specify the root endpoint as a property: ```$shipstation->orders->post($options, 'createorders')``` 74 | 75 | There are models that contain all of the properties available via the API. These models will be converted to arrays when passed to the API. 76 | 77 | An example on how to create a new order to be shipped: 78 | ```php 79 | $shipStation = $this->app['LaravelShipStation\ShipStation']; 80 | 81 | $address = new LaravelShipStation\Models\Address(); 82 | 83 | $address->name = "Joe Campo"; 84 | $address->street1 = "123 Main St"; 85 | $address->city = "Cleveland"; 86 | $address->state = "OH"; 87 | $address->postalCode = "44127"; 88 | $address->country = "US"; 89 | $address->phone = "2165555555"; 90 | 91 | $item = new LaravelShipStation\Models\OrderItem(); 92 | 93 | $item->lineItemKey = '1'; 94 | $item->sku = '580123456'; 95 | $item->name = "Awesome sweater."; 96 | $item->quantity = '1'; 97 | $item->unitPrice = '29.99'; 98 | $item->warehouseLocation = 'Warehouse A'; 99 | 100 | $order = new LaravelShipStation\Models\Order(); 101 | 102 | $order->orderNumber = '1'; 103 | $order->orderDate = '2016-05-09'; 104 | $order->orderStatus = 'awaiting_shipment'; 105 | $order->amountPaid = '29.99'; 106 | $order->taxAmount = '0.00'; 107 | $order->shippingAmount = '0.00'; 108 | $order->internalNotes = 'A note about my order.'; 109 | $order->billTo = $address; 110 | $order->shipTo = $address; 111 | $order->items[] = $item; 112 | 113 | // This will var_dump the newly created order, and order should be wrapped in an array. 114 | var_dump($shipStation->orders->post($order, 'createorder')); 115 | // or with the helper: $shipStation->orders->create($order); would be the same. 116 | ``` 117 | ### DELETE 118 | ```php 119 | $shipStation->{$endpoint}->delete($resourceEndPoint); 120 | ``` 121 | Example of deleting an order by it's order ID: 122 | ```php 123 | $shipStation->orders->delete($orderId); 124 | ``` 125 | ### UPDATE 126 | ```php 127 | $shipStation->{$endpoint}->update($query = [], $resourceEndPoint); 128 | ``` 129 | ## Simple Wrapper Helpers 130 | Helpers are located in ```/src/Helpers``` and will be named after the endpoint. Currently there is only a helper for the /orders endpoint and /shipments endpint. I will be adding more; feel free to send a PR with any you use. 131 | 132 | Check to see if an order already exists in ShipStation via an Order Number: 133 | 134 | ```php 135 | $orderExists = $shipStation->orders->existsByOrderNumber($orderNumber) // returns bool 136 | ``` 137 | 138 | > Note: When using the orderNumber query parameter ShipStation will return any order that contains the search term. e.g. orderNumber = 1 will return any order that CONTAINS 1 in ascending order and not an exact match to the query. If you have two orders 123, and 1234 in your ShipStation and call $shipStation->orders->get(['orderNumber' => 123]); you will return both orders. 139 | 140 | Check how many orders are in ```awaiting_fulfillment``` status: 141 | ```php 142 | $count = $shipStation->orders->awaitingShipmentCount(); // returns int 143 | ``` 144 | Create an order in ShipStation: 145 | ```php 146 | $newOrder = $shipStation->orders->create($order); 147 | ``` 148 | Get the shipments for a specific order number. 149 | ```php 150 | $shipments = $shipStation->shipments->forOrderNumber($orderNumber); 151 | ``` 152 | 153 | ## ShipStation API Rate Limit 154 | ShipStation only allows for 40 API calls that resets every 60 seconds (or 1 call every 1.5 seconds). By default, LaravelShipStation will protect against any calls being rate limited by pausing when we are averaging more than 1 call every 1.5 seconds. 155 | 156 | Once a request has been made, information about the current rate limiting values can be accessed using the following methods: 157 | 158 | Get the maximum number of requests that can be sent per window: 159 | ```php 160 | // integer 161 | $shipStation->getMaxAllowedRequests() 162 | ``` 163 | 164 | Get the remaining number of requests that can be sent in the current window: 165 | ```php 166 | // integer 167 | $shipStation->getRemainingRequests() 168 | ``` 169 | 170 | Get the number of seconds remaining until the next window begins: 171 | ```php 172 | // integer 173 | $shipStation->getSecondsUntilReset() 174 | ``` 175 | 176 | Check if requests are currently being rate limited: 177 | ```php 178 | // boolean 179 | $shipStation->isRateLimited() 180 | ``` 181 | 182 | ## Tests 183 | Tests can be ran using ```phpunit```. 184 | Please note that tests will create an order, check the order, and delete the order in your production environment. By default, tests are disabled. If you would like to run the tests edit the ```phpunit.xml``` file to set the environment variable ```SHIPSTATION_TESTING``` to ```true``` and set your API Key & Secret Key. 185 | ## Contribution 186 | Pull requests are most certainly welcomed! This is a WIP. 187 | ## License 188 | The MIT License (MIT). Please see [License File](https://github.com/joecampo/laravel-shipstation/blob/master/LICENSE) for more information. 189 | 190 | [ico-version]: https://img.shields.io/packagist/v/campo/laravel-shipstation.svg?style=flat-square 191 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 192 | [link-packagist]: https://packagist.org/packages/campo/laravel-shipstation 193 | [packagist-downloads]: https://img.shields.io/packagist/dt/campo/laravel-shipstation.svg 194 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "campo/laravel-shipstation", 3 | "description": "ShipStation API wrapper for Laravel", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Joe Campo", 8 | "email": "jcampo@gmail.com" 9 | } 10 | ], 11 | "autoload" : { 12 | "psr-4": { 13 | "LaravelShipStation\\": "src/" 14 | } 15 | }, 16 | "extra": { 17 | "laravel": { 18 | "providers": [ 19 | "LaravelShipStation\\ShipStationServiceProvider" 20 | ] 21 | } 22 | }, 23 | "require": { 24 | "guzzlehttp/guzzle": "^6.5|^7.4" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^5.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | env('SS_URL', 'https://ssapi.shipstation.com'), 5 | 'apiKey' => env('SS_KEY', ''), 6 | 'partnerApiKey' => env('SS_PARTNER_KEY', ''), 7 | 'apiSecret' => env('SS_SECRET', ''), 8 | ]; 9 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests/. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Helpers/Endpoint.php: -------------------------------------------------------------------------------- 1 | api = $api; 18 | } 19 | 20 | /** 21 | * Get a resource using the assigned endpoint ($this->api->endpoint). 22 | * 23 | * @param array $options 24 | * @param string $endpoint 25 | * @return \stdClass 26 | */ 27 | public function get($options = [], $endpoint = '') 28 | { 29 | return $this->api->get($options, $endpoint); 30 | } 31 | 32 | /** 33 | * Post to a resource using the assigned endpoint ($this->api->endpoint). 34 | * 35 | * @param array $options 36 | * @param string $endpoint 37 | * @return \stdClass 38 | */ 39 | public function post($options = [], $endpoint = '') 40 | { 41 | return $this->api->post($options, $endpoint); 42 | } 43 | 44 | /** 45 | * Delete a resource using the assigned endpoint ($this->api->endpoint). 46 | * 47 | * @param string $endpoint 48 | * @return \stdClass 49 | */ 50 | public function delete($endpoint = '') 51 | { 52 | return $this->api->delete($endpoint); 53 | } 54 | 55 | /** 56 | * Update a resource using the assigned endpoint ($this->api->endpoint). 57 | * 58 | * @param array $options 59 | * @param string $endpoint 60 | * @return \stdClass 61 | */ 62 | public function update($options = [], $endpoint = '') 63 | { 64 | return $this->api->update($options, $endpoint); 65 | } 66 | 67 | 68 | /** 69 | * Get the order the orderId from an orderNumber. 70 | * 71 | * @param mixed $orderNumber 72 | * @return int|null 73 | */ 74 | public function getOrderId($orderNumber) 75 | { 76 | $pages = $this->getTotalPages($orderNumber); 77 | 78 | foreach (range(1, $pages) as $i) { 79 | $response = $this->api->client->request('GET', "/orders/", [ 80 | 'query' => [ 81 | 'orderNumber' => $orderNumber, 82 | 'page' => $i 83 | ] 84 | ]); 85 | 86 | $this->api->sleepIfRateLimited($response); 87 | 88 | $data = json_decode($response->getBody()->getContents()); 89 | 90 | $orders = isset($data->orders) ? $data->orders : []; 91 | 92 | foreach ($orders as $order) { 93 | if ($order->orderNumber === $orderNumber) { 94 | return $order->orderId; 95 | } 96 | } 97 | } 98 | 99 | return null; 100 | } 101 | 102 | /** 103 | * Get the total number of pages possible to find the inputted order number. 104 | * 105 | * @param mixed $orderNumber 106 | * @return int 107 | */ 108 | private function getTotalPages($orderNumber) 109 | { 110 | $response = $this->api->client->request('GET', "/orders/", [ 111 | 'query' => ['orderNumber' => $orderNumber] 112 | ]); 113 | 114 | $data = json_decode($response->getBody()->getContents()); 115 | 116 | return isset($data->pages) ? $data->pages : 0; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Helpers/Orders.php: -------------------------------------------------------------------------------- 1 | post($order, 'createorder'); 15 | } 16 | 17 | /** 18 | * Does the specified order exist by the given order number? 19 | * 20 | * @param mixed $orderNumber 21 | * @return bool 22 | */ 23 | public function existsByOrderNumber($orderNumber) 24 | { 25 | $orderId = $this->getOrderId($orderNumber); 26 | 27 | return $orderId ? true : false; 28 | } 29 | 30 | /** 31 | * How many orders are awaiting shipment? 32 | * 33 | * @return int|null 34 | */ 35 | public function awaitingShipmentCount() 36 | { 37 | $count = $this->get([ 38 | 'orderStatus' => 'awaiting_shipment' 39 | ]); 40 | 41 | return isset($count->total) ? $count->total : null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Helpers/Shipments.php: -------------------------------------------------------------------------------- 1 | getOrderId($orderNumber); 15 | 16 | if (!$orderId) { 17 | return $this->emptyShipment(); 18 | } 19 | 20 | return $this->get(['orderId' => $orderId]); 21 | } 22 | 23 | /** 24 | * An empty shipment in ShipStation's format. 25 | * 26 | * @return \stdClass 27 | */ 28 | private function emptyShipment() 29 | { 30 | $shipments = new \stdClass(); 31 | 32 | $shipments->shipments = []; 33 | $shipments->total = 0; 34 | $shipments->page = 1; 35 | $shipments->pages = 0; 36 | 37 | return $shipments; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Models/Address.php: -------------------------------------------------------------------------------- 1 | Non-Delivery option for international shipment. Available options are: 22 | * "return_to_sender" or "treat_as_abandoned". Please note: If the shipment is created 23 | * through the Orders/CreateLabelForOrder endpoint and the nonDelivery field is not 24 | * specified then value defaults based on the International Setting in the UI. 25 | * If the call is being made to the Shipments/CreateLabel endpoint and 26 | * the nonDelivery field is not specified then the value will default 27 | * to "return_to_sender" 28 | */ 29 | public $nonDelivery; 30 | } 31 | -------------------------------------------------------------------------------- /src/Models/ItemOption.php: -------------------------------------------------------------------------------- 1 | base_uri = $apiURL; 68 | 69 | $headers = [ 70 | 'Authorization' => 'Basic '.base64_encode("{$apiKey}:{$apiSecret}"), 71 | ]; 72 | 73 | if (! empty($partnerApiKey)) { 74 | $headers['x-partner'] = $partnerApiKey; 75 | } 76 | 77 | $this->client = new Client([ 78 | 'base_uri' => $this->base_uri, 79 | 'headers' => $headers, 80 | ]); 81 | } 82 | 83 | /** 84 | * Get a resource using the assigned endpoint ($this->endpoint). 85 | * 86 | * @param array $options 87 | * @param string $endpoint 88 | * @return \stdClass 89 | */ 90 | public function get($options = [], $endpoint = '') 91 | { 92 | $response = $this->client->request('GET', "{$this->endpoint}{$endpoint}", ['query' => $options]); 93 | 94 | $this->sleepIfRateLimited($response); 95 | 96 | return json_decode($response->getBody()->getContents()); 97 | } 98 | 99 | /** 100 | * Post to a resource using the assigned endpoint ($this->endpoint). 101 | * 102 | * @param array $options 103 | * @param string $endpoint 104 | * @return \stdClass 105 | */ 106 | public function post($options = [], $endpoint = '') 107 | { 108 | $response = $this->client->request('POST', "{$this->endpoint}{$endpoint}", ['json' => $options]); 109 | 110 | $this->sleepIfRateLimited($response); 111 | 112 | return json_decode($response->getBody()->getContents()); 113 | } 114 | 115 | /** 116 | * Delete a resource using the assigned endpoint ($this->endpoint). 117 | * 118 | * @param string $endpoint 119 | * @return \stdClass 120 | */ 121 | public function delete($endpoint = '') 122 | { 123 | $response = $this->client->request('DELETE', "{$this->endpoint}{$endpoint}"); 124 | 125 | $this->sleepIfRateLimited($response); 126 | 127 | return json_decode($response->getBody()->getContents()); 128 | } 129 | 130 | /** 131 | * Update a resource using the assigned endpoint ($this->endpoint). 132 | * 133 | * @param array $options 134 | * @param string $endpoint 135 | * @return \stdClass 136 | */ 137 | public function update($options = [], $endpoint = '') 138 | { 139 | $response = $this->client->request('PUT', "{$this->endpoint}{$endpoint}", ['json' => $options]); 140 | 141 | $this->sleepIfRateLimited($response); 142 | 143 | return json_decode($response->getBody()->getContents()); 144 | } 145 | 146 | /** 147 | * Get the maximum number of requests that can be sent per window. 148 | * 149 | * @return int 150 | */ 151 | public function getMaxAllowedRequests() 152 | { 153 | return $this->maxAllowedRequests; 154 | } 155 | 156 | /** 157 | * Get the remaining number of requests that can be sent in the current window. 158 | * 159 | * @return int 160 | */ 161 | public function getRemainingRequests() 162 | { 163 | return $this->remainingRequests; 164 | } 165 | 166 | /** 167 | * Get the number of seconds remaining until the next window begins. 168 | * 169 | * @return int 170 | */ 171 | public function getSecondsUntilReset() 172 | { 173 | return $this->secondsUntilReset; 174 | } 175 | 176 | /** 177 | * Are we currently rate limited? 178 | * We are if there are no more requests allowed in the current window. 179 | * 180 | * @return bool 181 | */ 182 | public function isRateLimited() 183 | { 184 | return $this->remainingRequests !== null && ! $this->remainingRequests; 185 | } 186 | 187 | /** 188 | * Check to see if we are about to rate limit and pause if necessary. 189 | * 190 | * @param Response $response 191 | */ 192 | public function sleepIfRateLimited(Response $response) 193 | { 194 | $this->maxAllowedRequests = (int) $response->getHeader('X-Rate-Limit-Limit')[0]; 195 | $this->remainingRequests = (int) $response->getHeader('X-Rate-Limit-Remaining')[0]; 196 | $this->secondsUntilReset = (int) $response->getHeader('X-Rate-Limit-Reset')[0]; 197 | 198 | if ($this->isRateLimited() || ($this->secondsUntilReset / $this->remainingRequests) > 1.5) { 199 | usleep(1500000); 200 | } 201 | } 202 | 203 | /** 204 | * Set our endpoint by accessing it via a property. 205 | * 206 | * @param string $property 207 | * @return $this 208 | */ 209 | public function __get($property) 210 | { 211 | if (in_array('/'.$property.'/', $this->endpoints)) { 212 | $this->endpoint = '/'.$property.'/'; 213 | } 214 | 215 | $className = 'LaravelShipStation\\Helpers\\'.ucfirst($property); 216 | 217 | if (class_exists($className)) { 218 | return new $className($this); 219 | } 220 | 221 | return $this; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/ShipStationServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__ . '/../config/config.php' => config_path('shipstation.php'), 18 | ]); 19 | } 20 | 21 | /** 22 | * Register any application services. 23 | * 24 | * @return void 25 | */ 26 | public function register() 27 | { 28 | $this->app->singleton(ShipStation::class, function ($app) { 29 | return new ShipStation( 30 | config('shipstation.apiKey'), 31 | config('shipstation.apiSecret'), 32 | config('shipstation.apiURL'), 33 | config('shipstation.partnerApiKey') 34 | ); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/ShipStationTest.php: -------------------------------------------------------------------------------- 1 | shipStation = new LaravelShipStation\ShipStation( 26 | getenv('KEY'), 27 | getenv('SECRET'), 28 | getenv('API_URL'), 29 | getenv('PARTNER_KEY') 30 | ); 31 | } 32 | 33 | /** @test */ 34 | public function endpoint_can_be_set() 35 | { 36 | $this->shipStation->shipments; 37 | 38 | $this->assertEquals('/shipments/', $this->shipStation->endpoint); 39 | } 40 | 41 | /** @test */ 42 | public function order_can_be_created() 43 | { 44 | $address = new LaravelShipStation\Models\Address(); 45 | 46 | $address->name = "Joe Campo"; 47 | $address->street1 = "123 Main St"; 48 | $address->city = "Cleveland"; 49 | $address->state = "OH"; 50 | $address->postalCode = "44127"; 51 | $address->country = "US"; 52 | $address->phone = "2165555555"; 53 | 54 | $item = new LaravelShipStation\Models\OrderItem(); 55 | 56 | $item->lineItemKey = '1'; 57 | $item->sku = '580123456'; 58 | $item->name = "Test product"; 59 | $item->quantity = '1'; 60 | $item->unitPrice = '29.99'; 61 | $item->warehouseLocation = 'Warehouse A'; 62 | 63 | $order = new LaravelShipStation\Models\Order(); 64 | 65 | $order->orderNumber = 'TestOrder'; 66 | $order->orderDate = '1999-01-01'; 67 | $order->orderStatus = 'awaiting_shipment'; 68 | $order->amountPaid = '0.00'; 69 | $order->taxAmount = '0.00'; 70 | $order->shippingAmount = '0.00'; 71 | $order->internalNotes = 'A note about my order.'; 72 | $order->billTo = $address; 73 | $order->shipTo = $address; 74 | $order->items[] = $item; 75 | 76 | $newOrder = $this->shipStation->orders->create($order); 77 | 78 | $this->assertEquals('TestOrder', $newOrder->orderNumber); 79 | } 80 | 81 | /** @test */ 82 | public function order_does_exist() 83 | { 84 | $this->assertTrue($this->shipStation->orders->existsByOrderNumber('TestOrder')); 85 | } 86 | 87 | /** @test */ 88 | public function order_has_internal_note() 89 | { 90 | $order = $this->shipStation->orders->get(['orderNumber' => 'TestOrder']); 91 | 92 | $this->assertEquals('A note about my order.', $order->orders[0]->internalNotes); 93 | } 94 | 95 | /** @test */ 96 | public function order_can_be_updated() 97 | { 98 | $order = $this->shipStation->orders->get(['orderNumber' => 'TestOrder'])->orders[0]; 99 | 100 | $order->internalNotes = 'testing an updated note.'; 101 | 102 | $updatedOrder = $this->shipStation->orders->post($order, 'createorder'); 103 | 104 | $this->assertEquals('testing an updated note.', $updatedOrder->internalNotes); 105 | } 106 | 107 | 108 | /** @test */ 109 | public function orders_are_awaiting_shipments() 110 | { 111 | $this->assertGreaterThan(0, $this->shipStation->orders->awaitingShipmentCount()); 112 | } 113 | 114 | /** @test */ 115 | public function order_is_deleted() 116 | { 117 | $orderId = $this->shipStation->orders->get(['orderNumber' => 'TestOrder'])->orders[0]->orderId; 118 | 119 | $this->shipStation->orders->delete($orderId); 120 | 121 | $this->assertFalse($this->shipStation->orders->existsByOrderNumber('TestOrder')); 122 | } 123 | 124 | /** @test */ 125 | public function rate_limits_are_set_after_request() 126 | { 127 | $this->shipStation->webhooks->get(); 128 | 129 | $this->assertGreaterThanOrEqual(0, $this->shipStation->getMaxAllowedRequests()); 130 | $this->assertGreaterThanOrEqual(0, $this->shipStation->getRemainingRequests()); 131 | $this->assertGreaterThanOrEqual(0, $this->shipStation->getSecondsUntilReset()); 132 | $this->assertInternalType('boolean', $this->shipStation->isRateLimited()); 133 | } 134 | 135 | /** @test */ 136 | public function partner_api_key_header_is_set_when_defined() 137 | { 138 | if (empty(getenv('PARTNER_KEY'))) { 139 | // nothing to test 140 | return; 141 | } 142 | 143 | $this->shipStation->webhooks->get(); 144 | 145 | $headers = $this->shipStation->request->getConfig('headers'); 146 | 147 | $this->assertArrayHasKey('x-partner', $headers); 148 | } 149 | } 150 | --------------------------------------------------------------------------------