├── .styleci.yml ├── src ├── Concerns │ └── Arrayable.php ├── Exceptions │ ├── CalculatorException.php │ ├── InvalidCalculatorPayloadException.php │ ├── InvalidVolumeException.php │ ├── InvalidResourceException.php │ ├── InvalidEnvironmentException.php │ └── ClientException.php ├── Validations │ ├── Location.php │ └── Number.php ├── Resources │ ├── Resource.php │ ├── Shipment │ │ ├── Package.php │ │ ├── Volume.php │ │ ├── Product.php │ │ └── Calculator.php │ └── Base.php ├── Enums │ ├── Environment.php │ ├── Endpoint.php │ └── Service.php ├── Shipment.php └── Interfaces │ └── Resources │ └── Shipment │ └── ICalculator.php ├── CHANGELOG.md ├── examples ├── products │ ├── example3.php │ ├── example1.php │ └── example2.php └── packages │ ├── example2.php │ ├── example1.php │ └── example3.php ├── .github └── workflows │ └── ci.yaml ├── composer.json ├── LICENSE.md ├── CONTRIBUTING.md └── README.md /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | disabled: 4 | - single_class_element_per_statement 5 | -------------------------------------------------------------------------------- /src/Concerns/Arrayable.php: -------------------------------------------------------------------------------- 1 | 0; 10 | } 11 | 12 | public static function isPositiveInteger(int $number): bool 13 | { 14 | return is_integer($number) && $number > 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/ClientException.php: -------------------------------------------------------------------------------- 1 | getResponse(); 12 | 13 | parent::__construct($response->getBody()->getContents(), $response->getStatusCode()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Enums/Endpoint.php: -------------------------------------------------------------------------------- 1 | 'https://melhorenvio.com.br', 9 | 'sandbox' => 'https://sandbox.melhorenvio.com.br', 10 | ]; 11 | 12 | const VERSIONS = [ 13 | 'production' => 'v2', 14 | 'sandbox' => 'v2', 15 | ]; 16 | 17 | const PRODUCTION = self::ENDPOINTS['production']; 18 | 19 | const SANDBOX = self::ENDPOINTS['sandbox']; 20 | } 21 | -------------------------------------------------------------------------------- /src/Enums/Service.php: -------------------------------------------------------------------------------- 1 | calculator(); 15 | 16 | // Define Payload origin and destiny. 17 | $calculator->postalCode('01010010', '20271130'); 18 | 19 | // Define Payload Product 20 | $calculator->addProduct(new Product(uniqid(), 40, 30, 50, 10.00, 100.0, 1)); 21 | 22 | // Performs calculation 23 | $quotations = $calculator->calculate(); 24 | } catch (Exception $exception) { 25 | //Proper exception context 26 | } 27 | 28 | print_r($quotations); 29 | exit; 30 | -------------------------------------------------------------------------------- /examples/packages/example2.php: -------------------------------------------------------------------------------- 1 | calculator(); 16 | 17 | // Builds the calculator payload. 18 | $calculator->postalCode('01010010', '20271130'); 19 | $calculator->addPackage(new Package(2, 10, 15, 1, 6.0)); 20 | $calculator->addServices(Service::CORREIOS_PAC, Service::CORREIOS_SEDEX, Service::CORREIOS_MINI); 21 | 22 | $quotations = $calculator->calculate(); 23 | } catch (Exception $exception) { 24 | //Proper exception context 25 | } 26 | 27 | print_r($quotations); 28 | exit; 29 | -------------------------------------------------------------------------------- /examples/products/example1.php: -------------------------------------------------------------------------------- 1 | calculator(); 15 | 16 | // Builds the calculator payload. 17 | $calculator->postalCode('01010010', '20271130'); 18 | 19 | $calculator->setOwnHand(); 20 | $calculator->setReceipt(false); 21 | $calculator->setCollect(false); 22 | 23 | $calculator->addProducts( 24 | new Product(uniqid(), 40, 30, 50, 10.00, 100.0, 1), 25 | new Product(uniqid(), 5, 1, 10, 0.1, 50.0, 1) 26 | ); 27 | 28 | $quotations = $calculator->calculate(); 29 | } catch (Exception $exception) { 30 | //Proper exception context 31 | } 32 | 33 | print_r($quotations); 34 | exit; 35 | -------------------------------------------------------------------------------- /src/Resources/Shipment/Package.php: -------------------------------------------------------------------------------- 1 | setHeight($height); 17 | $this->setWidth($width); 18 | $this->setLength($length); 19 | $this->setWeight($weight); 20 | $this->setInsurance($insurance); 21 | } 22 | 23 | public function getInsurance(): float 24 | { 25 | return $this->insurance; 26 | } 27 | 28 | public function setInsurance(float $insurance) 29 | { 30 | $this->insurance = $insurance; 31 | } 32 | 33 | public function toArray(): array 34 | { 35 | return [ 36 | 'height' => $this->getHeight(), 37 | 'width' => $this->getWidth(), 38 | 'length' => $this->getLength(), 39 | 'weight' => $this->getWeight(), 40 | 'insurance' => $this->getInsurance(), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI - Continuous Integration 2 | on: 3 | pull_request: 4 | types: [ opened, ready_for_review, synchronize, reopened ] 5 | jobs: 6 | phpunit: 7 | name: PHPUnit 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | php-version: 14 | - "7.4" 15 | - "8.0" 16 | - "8.1" 17 | dependency-versions: 18 | - "lowest" 19 | - "highest" 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Setup PHP 26 | uses: shivammathur/setup-php@v2 27 | with: 28 | php-version: ${{ matrix.php-version }} 29 | coverage: none 30 | 31 | - name: Install composer dependencies 32 | uses: ramsey/composer-install@v2 33 | with: 34 | dependency-versions: ${{ matrix.dependency-versions }} 35 | 36 | - name: Run PHPUnit 37 | run: vendor/bin/phpunit tests 38 | -------------------------------------------------------------------------------- /examples/packages/example1.php: -------------------------------------------------------------------------------- 1 | calculator(); 16 | 17 | //Builds calculator payload 18 | $calculator->postalCode('01010010', '20271130'); 19 | 20 | $calculator->setOwnHand(); 21 | $calculator->setReceipt(false); 22 | $calculator->setCollect(false); 23 | 24 | $calculator->addPackages( 25 | new Package(12, 4, 17, 0.1, 6.0), 26 | new Package(12, 4, 17, 0.1, 6.0), 27 | new Package(12, 4, 17, 0.1, 6.0), 28 | new Package(12, 4, 17, 0.1, 6.0) 29 | ); 30 | 31 | $calculator->addServices( 32 | Service::CORREIOS_PAC, Service::CORREIOS_SEDEX, Service::JADLOG_PACKAGE, Service::JADLOG_COM, Service::AZULCARGO_AMANHA 33 | ); 34 | 35 | $quotations = $calculator->calculate(); 36 | }catch (Exception $exception) { 37 | //Proper exception context 38 | } 39 | 40 | print_r($quotations); 41 | exit; 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melhorenvio/shipment-sdk-php", 3 | "description": "Este pacote é responsável por realizar o cálculo de cotações.", 4 | "keywords": [ 5 | "melhorenvio", 6 | "shipment-sdk-php" 7 | ], 8 | "homepage": "https://github.com/melhorenvio/shipment-sdk-php", 9 | "license": "MIT", 10 | "type": "library", 11 | "authors": [ 12 | { 13 | "name": "Melhor Envio", 14 | "email": "tecnologia@melhorenvio.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^7.4|^8.0|^8.1", 20 | "ext-json": "*", 21 | "guzzlehttp/guzzle": "^7.0", 22 | "symfony/polyfill-php74": "^1.23" 23 | }, 24 | "require-dev": { 25 | "mockery/mockery": "^1.3.6", 26 | "phpunit/phpunit": "^9.5" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "MelhorEnvio\\": "src" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "MelhorEnvio\\Tests\\": "tests" 36 | } 37 | }, 38 | "scripts": { 39 | "test": "vendor/bin/phpunit", 40 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 41 | }, 42 | "config": { 43 | "sort-packages": true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/products/example2.php: -------------------------------------------------------------------------------- 1 | calculator(); 16 | 17 | // Builds the calculator payload. 18 | $calculator->postalCode('01010010', '20271130'); 19 | 20 | $products = [ 21 | ["id" => "z", "width" => 30, "height" => 40, "length" => 50, "weight" => 10.00, "quantity" => 10, "insurance_value" => 100.00], 22 | ["id" => "y", "width" => 1, "height" => 5, "length" => 10, "weight" => 0.10, "quantity" => 10, "insurance_value" => 50.0], 23 | ["id" => "x", "width" => 18, "height" => 12, "length" => 4, "weight" => 0.050, "quantity" => 1, "insurance_value" => 65.90], 24 | ]; 25 | 26 | foreach ($products as $product) { 27 | $calculator->addProduct( 28 | new Product($product['id'], $product['height'], $product['width'], $product['length'], $product['weight'], $product['insurance_value'], $product['quantity']) 29 | ); 30 | } 31 | 32 | $calculator->addServices(Service::JADLOG_PACKAGE, Service::JADLOG_COM, Service::VIABRASIL_RODOVIARIO, Service::LATAMCARGO_PROXIMODIA); 33 | 34 | $quotations = $calculator->calculate(); 35 | } catch (Exception $exception) { 36 | //Proper exception context 37 | } 38 | 39 | print_r($quotations); 40 | exit; 41 | -------------------------------------------------------------------------------- /examples/packages/example3.php: -------------------------------------------------------------------------------- 1 | calculator(); 16 | 17 | // Define Payload origin and destiny. 18 | $calculator->from('01010010'); 19 | $calculator->to('20271130'); 20 | 21 | // Define Payload Options 22 | $calculator->setReceipt(); 23 | $calculator->setOwnHand(); 24 | $calculator->setCollect(); 25 | 26 | // Define Payload Packages 27 | $packages = [ 28 | ["weight" => 0.1, "width" => 2, "height" => 5, "length" => 10, "insurance" => 50], 29 | ["weight" => 10, "width" => 34, "height" => 40, "length" => 50, "insurance" => 50] 30 | ]; 31 | 32 | foreach ($packages as $package) { 33 | $calculator->addPackage( 34 | new Package($package['height'], $package['width'], $package['length'], $package['weight'], $package['insurance']) 35 | ); 36 | } 37 | 38 | // Define Payload Services 39 | $calculator->addServices(Service::CORREIOS_MINI, Service::CORREIOS_SEDEX, Service::CORREIOS_PAC); 40 | 41 | // Performs calculation 42 | $quotations = $calculator->calculate(); 43 | } catch (Exception $exception) { 44 | //Proper exception context 45 | } 46 | 47 | foreach ($quotations as $quotation) { 48 | print("Service: " . $quotation['name'] . " "); 49 | 50 | if (isset($quotation['error'])) { 51 | echo $quotation['error']; 52 | continue; 53 | } 54 | 55 | print("Custom Price: " . $quotation['custom_price'] . " "); 56 | print("Custom Delivery Time: " . $quotation['custom_delivery_time'] . PHP_EOL); 57 | } 58 | -------------------------------------------------------------------------------- /src/Resources/Base.php: -------------------------------------------------------------------------------- 1 | token = $token; 23 | 24 | $this->setEnvironment($environment ? $environment : Environment::SANDBOX); 25 | 26 | $this->http = new Client([ 27 | 'base_uri' => Endpoint::ENDPOINTS[$this->environment] . '/api/' . Endpoint::VERSIONS[$this->environment] . '/', 28 | 'headers' => [ 29 | 'Authorization' => 'Bearer ' . $this->token, 30 | 'Accept' => 'application/json', 31 | ] 32 | ]); 33 | } 34 | 35 | public function getEnvironment(): string 36 | { 37 | return $this->environment; 38 | } 39 | 40 | public function setEnvironment(string $environment): void 41 | { 42 | if (! in_array($environment, Environment::ENVIRONMENTS)) { 43 | throw new InvalidEnvironmentException; 44 | } 45 | 46 | $this->environment = $environment; 47 | } 48 | 49 | public function getHttp(): ClientInterface 50 | { 51 | return $this->http; 52 | } 53 | 54 | /** 55 | * @throws Exception 56 | */ 57 | public function setHttp(ClientInterface $http): void 58 | { 59 | if (! $http instanceof ClientInterface) { 60 | throw new Exception('The parameter passed is not an instance of ' . ClientInterface::class); 61 | } 62 | 63 | $this->http = $http; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Resources/Shipment/Volume.php: -------------------------------------------------------------------------------- 1 | weight; 22 | } 23 | 24 | public function setWeight(float $weight) 25 | { 26 | $this->validateNumericArgument($weight, 'weight'); 27 | 28 | $this->weight = $weight; 29 | } 30 | 31 | public function getLength(): float 32 | { 33 | return $this->length; 34 | } 35 | 36 | public function setLength(float $length) 37 | { 38 | $this->validateNumericArgument($length, 'length'); 39 | 40 | $this->length = $length; 41 | } 42 | 43 | public function getWidth(): float 44 | { 45 | return $this->width; 46 | } 47 | 48 | public function setWidth(float $width) 49 | { 50 | $this->validateNumericArgument($width, 'width'); 51 | 52 | $this->width = $width; 53 | } 54 | 55 | public function getHeight(): float 56 | { 57 | return $this->height; 58 | } 59 | 60 | public function setHeight(float $height) 61 | { 62 | $this->validateNumericArgument($height, 'height'); 63 | 64 | $this->height = $height; 65 | } 66 | 67 | protected function validateNumericArgument(float $number, string $argument) 68 | { 69 | if (! Number::isPositive($number)) { 70 | throw new InvalidArgumentException($argument); 71 | } 72 | } 73 | 74 | public function isValid(): bool 75 | { 76 | return ! empty($this->getHeight()) 77 | && ! empty($this->getWidth()) 78 | && ! empty($this->getLength()) 79 | && ! empty($this->getWeight()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Melhor Envio 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 | 23 | É concedida a permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste *software* e dos arquivos de documentação associados (ao "*Software*"), para lidar no *software* sem restrição, incluindo, sem limitação, os direitos de usar, copiar, modificar, mesclar, publicar, distribuir, sublienciar e/ou vender cópias do *software* e permitir pessoas a quem o *software* está fornecido para tal, sujeito às seguintes condições: 24 | 25 | O aviso de direitos autorais acima e este aviso de permissão devem ser incluídos em todas as cópias ou partes substanciais do *software*. 26 | 27 | O *SOFTWARE* É FORNECIDO "TAL COMO ESTÁ", SEM GARANTIA DE QUALQUER TIPO EXPRESSA OU IMPLÍCITA, INCLUINDO MAS NÃO SE LIMITANDO A GARANTIAS DE COMERCIALIZAÇÃO, APTIDÃO PARA UM OBJETIVO ESPECÍFICO E NÃO INFRACÇÃO. EM NENHUM CASO OS AUTORES E TITULARES DOS DIREITOS AUTORAIS SÃO RESPONSÁVEIS POR QUALQUER RECLAMAÇÃO, DANOS OU RESPONSABILIDADE, SEJA EM AÇÃO DE CONTRATO, *TORT* OU DE OUTRA FORMA, DECORRENTE DE, COM OU SEM CONEXÃO COM O *SOFTWARE* OU O USO DE OUTRAS FORMAS DE NEGÓCIO NO *SOFTWARE*. -------------------------------------------------------------------------------- /src/Resources/Shipment/Product.php: -------------------------------------------------------------------------------- 1 | setId($id); 26 | $this->setHeight($height); 27 | $this->setWidth($width); 28 | $this->setLength($length); 29 | $this->setWeight($weight); 30 | $this->setInsuranceValue($insuranceValue); 31 | $this->setQuantity($quantity); 32 | } 33 | 34 | public function getId(): string 35 | { 36 | return $this->id; 37 | } 38 | 39 | public function setId(string $id) 40 | { 41 | $this->id = (string) $id; 42 | } 43 | 44 | public function getQuantity(): int 45 | { 46 | return $this->quantity; 47 | } 48 | 49 | public function setQuantity(int $quantity) 50 | { 51 | if (! Number::isPositiveInteger($quantity)) { 52 | throw new InvalidArgumentException("quantity"); 53 | } 54 | 55 | $this->quantity = $quantity; 56 | } 57 | 58 | public function getInsuranceValue(): float 59 | { 60 | return $this->insuranceValue; 61 | } 62 | 63 | public function setInsuranceValue(float $insuranceValue) 64 | { 65 | $this->validateNumericArgument($insuranceValue, 'insurance_value'); 66 | 67 | $this->insuranceValue = $insuranceValue; 68 | } 69 | 70 | public function toArray(): array 71 | { 72 | return [ 73 | 'id' => $this->getId(), 74 | 'height' => $this->getHeight(), 75 | 'width' => $this->getWidth(), 76 | 'length' => $this->getLength(), 77 | 'weight' => $this->getWeight(), 78 | 'insurance_value' => $this->getInsuranceValue(), 79 | 'quantity' => $this->getQuantity(), 80 | ]; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Interfaces/Resources/Shipment/ICalculator.php: -------------------------------------------------------------------------------- 1 | resource = $resource; 33 | } 34 | 35 | /** 36 | * @throws InvalidArgumentException 37 | */ 38 | public function from(string $postalCode) 39 | { 40 | $this->addPostalCodeInPayload('from', $postalCode); 41 | } 42 | 43 | /** 44 | * @throws InvalidArgumentException 45 | */ 46 | public function to(string $postalCode) 47 | { 48 | $this->addPostalCodeInPayload('to', $postalCode); 49 | } 50 | 51 | public function postalCode(string $from, string $to) 52 | { 53 | $this->addPostalCodeInPayload('from', $from); 54 | $this->addPostalCodeInPayload('to', $to); 55 | } 56 | 57 | /** 58 | * @throws InvalidArgumentException 59 | */ 60 | protected function addPostalCodeInPayload(string $key, string $postalCode) 61 | { 62 | if (! $this->isValidPostalCode($postalCode)) { 63 | throw new InvalidArgumentException($key); 64 | } 65 | 66 | $this->payload[$key]['postal_code'] = $postalCode; 67 | } 68 | 69 | /** 70 | * @throws InvalidArgumentException 71 | */ 72 | public function addProducts(Product $products) 73 | { 74 | $products = is_array($products) ? $products : func_get_args(); 75 | 76 | foreach ($products as $product) { 77 | $this->addProduct($product); 78 | } 79 | } 80 | 81 | /** 82 | * @throws InvalidArgumentException 83 | */ 84 | public function addPackages(Package $packages) 85 | { 86 | $packages = is_array($packages) ? $packages : func_get_args(); 87 | 88 | foreach ($packages as $package) { 89 | $this->addPackage($package); 90 | } 91 | } 92 | 93 | /** 94 | * @throws InvalidVolumeException 95 | */ 96 | public function addPackage(Package $package) 97 | { 98 | if (! $this->isValidPackage($package)) { 99 | throw new InvalidVolumeException('package'); 100 | } 101 | 102 | $this->payload['volumes'][] = $package->toArray(); 103 | } 104 | 105 | /** 106 | * @throws InvalidVolumeException 107 | */ 108 | public function addProduct(Product $product) 109 | { 110 | if (! $this->isValidProduct($product)) { 111 | throw new InvalidVolumeException('product'); 112 | } 113 | 114 | $this->payload['products'][] = $product->toArray(); 115 | } 116 | 117 | /** 118 | * @throws InvalidArgumentException 119 | */ 120 | public function addServices($services) 121 | { 122 | $services = is_array($services) ? $services : func_get_args(); 123 | 124 | foreach ($services as $service) { 125 | $this->addService($service); 126 | } 127 | } 128 | 129 | /** 130 | * @throws InvalidArgumentException 131 | */ 132 | public function addService(int $service) 133 | { 134 | if (! $this->isValidService($service)) { 135 | throw new InvalidArgumentException('service'); 136 | } 137 | 138 | if (! isset($this->payload['services'])) { 139 | $this->payload['services'] = $service; 140 | } else { 141 | $this->payload['services'] .= ',' . $service; 142 | } 143 | } 144 | 145 | /** 146 | * @throws InvalidArgumentException 147 | */ 148 | public function setReceipt(bool $receipt = true) 149 | { 150 | if (! is_bool($receipt)) { 151 | throw new InvalidArgumentException('receipt'); 152 | } 153 | 154 | $this->payload['options']['receipt'] = $receipt; 155 | } 156 | 157 | /** 158 | * @throws InvalidArgumentException 159 | */ 160 | public function setOwnHand(bool $ownHand = true) 161 | { 162 | if (! is_bool($ownHand)) { 163 | throw new InvalidArgumentException('own_hand'); 164 | } 165 | 166 | $this->payload['options']['own_hand'] = $ownHand; 167 | } 168 | 169 | /** 170 | * @throws InvalidArgumentException 171 | */ 172 | public function setCollect(bool $collect = true) 173 | { 174 | if (! is_bool($collect)) { 175 | throw new InvalidArgumentException('collect'); 176 | } 177 | 178 | $this->payload['options']['collect'] = $collect; 179 | } 180 | 181 | public function isValidPostalCode(string $postalCode): bool 182 | { 183 | return Location::isPostalCode($postalCode); 184 | } 185 | 186 | public function isValidProduct(Product $product): bool 187 | { 188 | return $product instanceof Product && $product->isValid(); 189 | } 190 | 191 | public function isValidPackage(Package $package): bool 192 | { 193 | return $package instanceof Package && $package->isValid(); 194 | } 195 | 196 | protected function isValidService(int $service): bool 197 | { 198 | return Number::isPositiveInteger($service); 199 | } 200 | 201 | protected function validatePayload(): void 202 | { 203 | if (empty($this->payload['from']['postal_code']) || empty($this->payload['to']['postal_code'])) { 204 | throw new InvalidCalculatorPayloadException('The CEP is invalid.'); 205 | } 206 | 207 | if (empty($this->payload['volumes']) && empty($this->payload['products'])) { 208 | throw new InvalidCalculatorPayloadException('There are no defined products or volumes.'); 209 | } 210 | 211 | if (! empty($this->payload['volumes']) && ! empty($this->payload['products'])) { 212 | throw new InvalidCalculatorPayloadException('Products and volumes cannot be defined together in the same payload.'); 213 | } 214 | } 215 | 216 | public function getPayload(): array 217 | { 218 | return $this->payload; 219 | } 220 | 221 | /** 222 | * @throws InvalidCalculatorPayloadException|CalculatorException 223 | */ 224 | public function calculate() 225 | { 226 | $this->validatePayload(); 227 | 228 | try { 229 | $response = $this->resource->getHttp()->post('me/shipment/calculate', [ 230 | 'json' => $this->payload, 231 | ]); 232 | 233 | return json_decode((string) $response->getBody(), true); 234 | } catch (ClientException $exception) { 235 | throw new CalculatorException($exception); 236 | } 237 | } 238 | 239 | public function __toString(): string 240 | { 241 | return json_encode($this->getPayload()); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shipment SDK - Serviço de cotações do Melhor Envio 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/melhorenvio/shipment-sdk-php.svg?style=flat-square)](https://packagist.org/packages/melhorenvio/shipment-sdk-php) 4 | [![Build Status](https://img.shields.io/travis/melhorenvio/shipment-sdk-php/master.svg?style=flat-square)](https://travis-ci.org/melhorenvio/shipment-sdk-php) 5 | [![Quality Score](https://img.shields.io/scrutinizer/g/melhorenvio/shipment-sdk-php.svg?style=flat-square)](https://scrutinizer-ci.com/g/melhorenvio/shipment-sdk-php) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/melhorenvio/shipment-sdk-php.svg?style=flat-square)](https://packagist.org/packages/melhorenvio/shipment-sdk-php) 7 | 8 | Agora ficou mais fácil ter o serviço de cotações do Melhor Envio no seu projeto de e-commerce. 9 | 10 | ## Índice 11 | 12 | * [Instalação](#instalação) 13 | * [Cofiguração Inicial](#configuração-inicial) 14 | * [Exemplos de uso](#exemplos-de-uso) 15 | * [Criação da calculadora](#criando-a-instância-calculadora) 16 | * [Adicionando CEPs de origem e destino](#adicionando-ceps-de-origem-e-destino) 17 | * [Produtos](#produtos) 18 | * [Adicionando os produtos para cotação](#adicionando-os-produtos-para-cotação) 19 | * [Pacotes](#pacotes) 20 | * [Adicionando os pacotes para cotação](#adicionando-os-pacotes-para-cotação) 21 | * [Adicionando os serviços das transportadoras](#adicionando-os-serviços-das-transportadoras) 22 | * [Adicionando serviços adicionais](#adicionando-serviços-adicionais) 23 | * [Retornando a cotação](#retornando-as-informações-da-cotação) 24 | * [Mais exemplos](#mais-exemplos) 25 | * [Testes](#testes) 26 | * [Changelog](#changelog) 27 | * [Contribuindo](#contribuindo) 28 | * [Segurança](#segurança) 29 | * [Créditos](#créditos) 30 | * [Licença](#licença) 31 | 32 | ## Dependências 33 | 34 | ### require 35 | * PHP 7.4, 8.0 ou 8.1 36 | * Ext-json = * 37 | * Guzzlehttp/guzzle >= 7.0 38 | * symfony/polyfill-php74 >= 1.23 39 | 40 | ### require-dev 41 | * mockery/mockery >= 1.3.6 42 | * phpunit/phpunit >= 9.5 43 | 44 | 45 | 46 | ## Instalação 47 | 48 | Você pode instalar o pacote via composer, rodando o seguinte comando: 49 | 50 | ```bash 51 | composer require melhorenvio/shipment-sdk-php 52 | ``` 53 | 54 | ## Configuração inicial 55 | 56 | A instância criada de Shipment permite que você passe como parâmetros o seu token e o ambiente que você trabalhará, assim terá a autenticação pronta. 57 | 58 | Lembrando que só será válido, se a criação do token pertencer ao mesmo ambiente passado como parâmetro. 59 | 60 | Se você ainda não fez o processo de autorização, o Melhor Envio possui outro pacote para facilitar o processo. Você pode acessá-lo clicando [aqui](https://bitbucket.org/melhor-envio/auth-sdk/src/master/). 61 | 62 | ```php 63 | require "vendor/autoload.php"; 64 | 65 | use MelhorEnvio\Shipment; 66 | use MelhorEnvio\Resources\Shipment\Package; 67 | use MelhorEnvio\Enums\Service; 68 | use MelhorEnvio\Enums\Environment; 69 | 70 | // Create Shipment Instance 71 | $shipment = new Shipment('your-token', Environment::PRODUCTION); 72 | ``` 73 | 74 | ## Exemplos de uso 75 | 76 | ### Criando a instância calculadora 77 | 78 | Neste exemplo você criará uma instância para calculadora no seu código. 79 | 80 | ```php 81 | // Create Calculator Instance 82 | $calculator = $shipment->calculator(); 83 | ``` 84 | 85 | ### Adicionando CEPs de origem e destino 86 | 87 | Nesta parte você deve definir os CEPs de origem e destino respectivamente. 88 | 89 | ```php 90 | // Builds calculator payload 91 | $calculator->postalCode('01010010', '20271130'); 92 | ``` 93 | 94 | ### Produtos 95 | 96 | #### Adicionando os produtos para cotação 97 | 98 | Nesta parte, você irá definir os produtos que servirão para a sua cotação as informações que devem ser passadas como parâmetro são as seguintes: 99 | 100 | * Altura 101 | * Largura 102 | * Comprimento 103 | * Peso 104 | * Valor segurado 105 | * Quantidade 106 | 107 | Lembrando que o valor segurado por padrão deve ser o valor do produto. 108 | 109 | ```php 110 | $calculator->addProducts( 111 | new Product(uniqid(), 40, 30, 50, 10.00, 100.0, 1), 112 | new Product(uniqid(), 5, 1, 10, 0.1, 50.0, 1) 113 | ); 114 | ``` 115 | 116 | ### Pacotes 117 | 118 | #### Adicionando os pacotes para cotação 119 | 120 | Nesta parte, você irá definir os pacotes que servirão para sua cotação, as informações que devem ser passadas como parâmetro são as seguintes: 121 | 122 | * Altura 123 | * Largura 124 | * Comprimento 125 | * Peso 126 | * Valor segurado 127 | 128 | **As dimensões sempre devem ser passadas em centímetros e o peso em quilogramas. São as unidades que o Melhor Envio opera.** 129 | 130 | Lembrando que o valor segurado por padrão deve ser o valor do produto em Reais. 131 | 132 | ```php 133 | $calculator->addPackages( 134 | new Package(12, 4, 17, 0.1, 6.0), 135 | new Package(12, 4, 17, 0.1, 6.0), 136 | new Package(12, 4, 17, 0.1, 6.0), 137 | new Package(12, 4, 17, 0.1, 6.0) 138 | ); 139 | ``` 140 | 141 | **É importante ressaltar que os métodos de PACOTES e PRODUTOS não poderão ser utilizados conjuntamente, devendo ser utilizado apenas um ou outro.** 142 | 143 | ### Adicionando os serviços das transportadoras 144 | 145 | Se você desejar customizar, nesta parte serão escolhidos os serviços das transportadoras que você deseja utilizar. Hoje, no Melhor Envio, estão disponíveis: 146 | 147 | * Correios 148 | * Jadlog 149 | * Via Brasil 150 | * Azul Cargo 151 | * Latam Cargo 152 | 153 | ```php 154 | $calculator->addServices( 155 | Service::CORREIOS_PAC, 156 | Service::CORREIOS_SEDEX, 157 | Service::CORREIOS_MINI, 158 | Service::JADLOG_PACKAGE, 159 | Service::JADLOG_COM, 160 | Service::AZULCARGO_AMANHA, 161 | Service::AZULCARGO_ECOMMERCE, 162 | Service::LATAMCARGO_JUNTOS, 163 | Service::VIABRASIL_RODOVIARIO 164 | ); 165 | ``` 166 | 167 | ### Adicionando serviços adicionais 168 | 169 | Se você desejar customizar, pode configurar alguns serviços adicionais na sua cotação, são eles: 170 | 171 | * Mão própria 172 | * Aviso de recebimento 173 | * Coleta 174 | 175 | Lembrando que a adição desses serviços podem gerar acréscimos no preço na hora da cotação. 176 | 177 | ```php 178 | $calculator->setOwnHand(); 179 | $calculator->setReceipt(); 180 | $calculator->setCollect(); 181 | ``` 182 | 183 | 184 | ### Retornando as informações da cotação 185 | 186 | Aqui você retornará as informações do payload montado. 187 | 188 | ```php 189 | $quotations = $calculator->calculate(); 190 | ``` 191 | 192 | ### Mais exemplos 193 | 194 | [Aqui você pode acessar mais exemplos de implementação](/examples) 195 | 196 | ### Testes 197 | 198 | Dentro do projeto você encontrará alguns documentos de teste baseados em testes unitários 199 | 200 | 201 | Você pode usar na aplicação tanto o comando: 202 | ``` bash 203 | composer test 204 | ``` 205 | Quanto o comando: 206 | ```bash 207 | vendor/bin/phpunit tests 208 | ``` 209 | 210 | ### Changelog 211 | 212 | Consulte [CHANGELOG](CHANGELOG.md) para mais informações de alterações recentes. 213 | 214 | ## Contribuindo 215 | 216 | Consulte [CONTRIBUTING](CONTRIBUTING.md) para mais detalhes. 217 | 218 | ### Segurança 219 | 220 | Se você descobrir algum problema de segurança, por favor, envie um e-mail para tecnologia@melhorenvio.com, ao invés de usar um *issue tracker*. 221 | 222 | ## Créditos 223 | 224 | - [Rodrigo Silveira](https://github.com/rodriigogs) 225 | - [Marçal Pizzi](https://github.com/marcalpizzi) 226 | - [Pedro Barros](https://github.com/pedrobarros05) 227 | 228 | ## Licença 229 | 230 | Melhor Envio. Consulte [Arquivo de licença](LICENSE.md) para mais informações. 231 | --------------------------------------------------------------------------------