From 1de24405f1f1feb71987b8ac6cf75a15190f441e Mon Sep 17 00:00:00 2001 From: domkrm Date: Tue, 2 Feb 2021 21:37:18 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 + LICENSE.md | 21 ++ README.md | 58 ++++ composer.json | 43 +++ config/letterxpress.php | 46 +++ src/Client.php | 100 +++++++ src/Exceptions/LxpException.php | 10 + src/Facades/LetterXpress.php | 19 ++ src/Models/Letter.php | 396 +++++++++++++++++++++++++ src/Providers/LetterXpressProvider.php | 42 +++ src/Requests/SetJob.php | 72 +++++ 11 files changed, 810 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 config/letterxpress.php create mode 100644 src/Client.php create mode 100644 src/Exceptions/LxpException.php create mode 100644 src/Facades/LetterXpress.php create mode 100644 src/Models/Letter.php create mode 100644 src/Providers/LetterXpressProvider.php create mode 100644 src/Requests/SetJob.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2130d09 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea +/vendor +composer.lock diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0e68335 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) CTSoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8280fee --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# Laravel LetterXpress + +This package connects Laravel to the LetterXpress API. + +## Install + +Install this package via Composer: + +``` +composer require ctsoft/laravel-letter-xpress +``` + +Add the following lines to your .env file and insert your own API credentials: + +``` +LETTER_XPRESS_API_USER=[username] +LETTER_XPRESS_API_KEY=[apikey] +``` + +If you want to use the sandbox add this line to your .env file: + +``` +LETTER_XPRESS_API_URL=https://sandbox.letterxpress.de/v1/ +``` + +## Usage + +To set a new job: + +```php +use CTSoft\Laravel\LetterXpress\Facades\LetterXpress; +use CTSoft\Laravel\LetterXpress\Models\Letter; + +$letter = (new Letter()) + ->setFile('document.pdf') // Use one of this functions + ->setDocument('PDF BINARY STRING') // to set the document + ->setColor(true) + ->setDuplex(true); +... + +$letter = LetterXpress::setJob($letter); +echo $letter->getJobId(); +echo $letter->getPrice(); +... +``` + +## Notes + +- Currently only set of a new job is supported +- Feel free to make a PR for additional features or ask for it via an issue + +## Security + +If you discover any security related issues, please email [security@ctsoft.de](mailto:security@ctsoft.de) instead of using the issue tracker. + +## License + +This package is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..9432158 --- /dev/null +++ b/composer.json @@ -0,0 +1,43 @@ +{ + "name": "ctsoft/laravel-letter-xpress", + "description": "LetterXpress connection for Laravel.", + "keywords": [ + "laravel", + "letterxpress" + ], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "CTSoft", + "email": "info@ctsoft.de" + } + ], + "require": { + "php": "^7.2", + "illuminate/config": "^7.0|^8.0", + "illuminate/contracts": "^7.0|^8.0", + "illuminate/http": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0" + }, + "autoload": { + "psr-4": { + "CTSoft\\Laravel\\LetterXpress\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "CTSoft\\Laravel\\LetterXpress\\Providers\\LetterXpressProvider" + ], + "aliases": { + "LetterXpress": "CTSoft\\Laravel\\LetterXpress\\Facades\\LetterXpress" + } + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/config/letterxpress.php b/config/letterxpress.php new file mode 100644 index 0000000..d829eb2 --- /dev/null +++ b/config/letterxpress.php @@ -0,0 +1,46 @@ + env('LETTER_XPRESS_API_URL', 'https://api.letterxpress.de/v1/'), + + /* + |-------------------------------------------------------------------------- + | API username + |-------------------------------------------------------------------------- + | + | This is the username of your LetterXpress account. + | + | Production: https://api.letterxpress.de/v1/ + | Sandbox: https://sandbox.letterxpress.de/v1/ + | + */ + + 'api_user' => env('LETTER_XPRESS_API_USER'), + + /* + |-------------------------------------------------------------------------- + | API key + |-------------------------------------------------------------------------- + | + | This is your personal API key. + | + | Refer to: https://www.letterxpress.de/account/api + | + */ + + 'api_key' => env('LETTER_XPRESS_API_KEY'), + +]; diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..f898380 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,100 @@ +apiUrl = $apiUrl; + $this->apiUser = $apiUser; + $this->apiKey = $apiKey; + } + + /** + * Set a new job. + * + * @param Letter $letter + * @return Letter + * @throws LxpException + */ + public function setJob(Letter $letter): Letter + { + return SetJob::getLetter( + $this->request('setJob', + SetJob::getPayload($letter) + ) + ); + } + + /** + * Perform a request to the LetterXpress API. + * + * @param string $function + * @param array $payload + * @return array + * @throws LxpException + */ + protected function request(string $function, array $payload): array + { + $url = Str::finish($this->apiUrl, '/') . $function; + + $payload = array_merge([ + 'auth' => [ + 'username' => $this->apiUser, + 'apikey' => $this->apiKey, + ], + ], $payload); + + try { + $response = Http::post($url, $payload)->throw()->json(); + } catch (HttpClientException $exception) { + throw new LxpException('HTTP request failed.', 0, $exception); + } + + if (is_null($response)) { + throw new LxpException('Invalid JSON response.'); + } + + if ($response['status'] !== 200) { + throw new LxpException($response['message'], $response['status']); + } + + return $response; + } +} diff --git a/src/Exceptions/LxpException.php b/src/Exceptions/LxpException.php new file mode 100644 index 0000000..523daf8 --- /dev/null +++ b/src/Exceptions/LxpException.php @@ -0,0 +1,10 @@ +setDocument(file_get_contents($file)); + } + + /** + * Get the document (file path or binary). + * + * @return string|null + */ + public function getDocument(): ?string + { + return $this->document; + } + + /** + * Set the document (binary). + * + * @param string $document + * @return Letter + */ + public function setDocument(string $document): Letter + { + $this->document = $document; + return $this; + } + + /** + * Get the receiver address. + * + * @return string|null + */ + public function getAddress(): ?string + { + return $this->address; + } + + /** + * Set the receiver address. + * + * @param string $address + * @return Letter + */ + public function setAddress(string $address): Letter + { + $this->address = $address; + return $this; + } + + /** + * Get if the letter should be printed in color. + * + * @return bool + */ + public function isColor(): bool + { + return $this->color; + } + + /** + * Set if the letter should be printed in color. + * + * @param bool $color + * @return Letter + */ + public function setColor(bool $color): Letter + { + $this->color = $color; + return $this; + } + + /** + * Get if the letter should be printed with duplex. + * + * @return bool + */ + public function isDuplex(): bool + { + return $this->duplex; + } + + /** + * set if the letter should be printed with duplex. + * + * @param bool $duplex + * @return Letter + */ + public function setDuplex(bool $duplex): Letter + { + $this->duplex = $duplex; + return $this; + } + + /** + * Get if the letter should be shipped international. + * + * @return bool + */ + public function isInternational(): bool + { + return $this->international; + } + + /** + * Set if the letter should be shipped international. + * + * @param bool $international + * @return Letter + */ + public function setInternational(bool $international): Letter + { + $this->international = $international; + return $this; + } + + /** + * Get if the letter should be shipped in a C4 shipping bag. + * + * @return bool + */ + public function isC4ShippingBag(): bool + { + return $this->c4ShippingBag; + } + + /** + * Set if the letter should be shipped in a C4 shipping bag. + * + * @param bool $c4ShippingBag + * @return Letter + */ + public function setC4ShippingBag(bool $c4ShippingBag): Letter + { + $this->c4ShippingBag = $c4ShippingBag; + return $this; + } + + /** + * Get the dispatch date. + * + * @return Carbon|null + */ + public function getDispatchDate(): ?Carbon + { + return $this->dispatchDate; + } + + /** + * Set the dispatch date. + * + * @param Carbon|null $dispatchDate + * @return Letter + */ + public function setDispatchDate(?Carbon $dispatchDate): Letter + { + $this->dispatchDate = $dispatchDate; + return $this; + } + + /** + * Get the job id. + * + * @return int|null + */ + public function getJobId(): ?int + { + return $this->jobId; + } + + /** + * Set the job id. + * + * @param int $jobId + * @return Letter + */ + public function setJobId(int $jobId): Letter + { + $this->jobId = $jobId; + return $this; + } + + /** + * Get the amount of pages. + * + * @return int|null + */ + public function getPages(): ?int + { + return $this->pages; + } + + /** + * Set the amount of pages. + * + * @param int $pages + * @return Letter + */ + public function setPages(int $pages): Letter + { + $this->pages = $pages; + return $this; + } + + /** + * Get the price of the job. + * + * @return float|null + */ + public function getPrice(): ?float + { + return $this->price; + } + + /** + * Set the price of the job. + * + * @param float $price + * @return Letter + */ + public function setPrice(float $price): Letter + { + $this->price = $price; + return $this; + } + + /** + * Get the status of the job. + * + * @return string|null + */ + public function getStatus(): ?string + { + return $this->status; + } + + /** + * Set the status of the job. + * + * @param string $status + * @return Letter + */ + public function setStatus(string $status): Letter + { + $this->status = $status; + return $this; + } + + /** + * Get the balance notice. + * + * @return string|null + */ + public function getBalanceNotice(): ?string + { + return $this->balanceNotice; + } + + /** + * Set the balance notice. + * + * @param string $balanceNotice + * @return Letter + */ + public function setBalanceNotice(string $balanceNotice): Letter + { + $this->balanceNotice = $balanceNotice; + return $this; + } + + /** + * Get the timer notice. + * + * @return string|null + */ + public function getTimerNotice(): ?string + { + return $this->timerNotice; + } + + /** + * Set the timer notice. + * + * @param string $timerNotice + * @return Letter + */ + public function setTimerNotice(string $timerNotice): Letter + { + $this->timerNotice = $timerNotice; + return $this; + } +} diff --git a/src/Providers/LetterXpressProvider.php b/src/Providers/LetterXpressProvider.php new file mode 100644 index 0000000..ab63442 --- /dev/null +++ b/src/Providers/LetterXpressProvider.php @@ -0,0 +1,42 @@ +mergeConfigFrom( + __DIR__ . '/../../config/letterxpress.php', 'letterxpress' + ); + + $this->app->singleton(Client::class, function (Application $app) { + return new Client( + $app['config']['letterxpress.api_url'], + $app['config']['letterxpress.api_user'], + $app['config']['letterxpress.api_key'] + ); + }); + } + + /** + * Bootstrap any package services. + * + * @return void + */ + public function boot(): void + { + $this->publishes([ + __DIR__ . '/../../config/letterxpress.php' => $this->app->configPath('letterxpress.php'), + ], 'config'); + } +} diff --git a/src/Requests/SetJob.php b/src/Requests/SetJob.php new file mode 100644 index 0000000..52522e7 --- /dev/null +++ b/src/Requests/SetJob.php @@ -0,0 +1,72 @@ +getDocument()); + + $payload = [ + 'letter' => [ + 'base64_file' => $document, + 'base64_checksum' => md5($document), + 'address' => $letter->getAddress() ?? 'read', + 'specification' => [ + 'color' => $letter->isColor() ? 4 : 1, + 'mode' => $letter->isDuplex() ? 'duplex' : 'simplex', + 'ship' => $letter->isInternational() ? 'international' : 'national', + 'c4' => $letter->isC4ShippingBag() ? 'y' : 'n', + ], + ], + ]; + + if (!is_null($letter->getDispatchDate())) { + $payload['letter']['dispatchdate'] = $letter->getDispatchDate()->format('d.m.Y'); + } + + return $payload; + } + + /** + * Get the letter. + * + * @param array $response + * @return Letter + */ + public static function getLetter(array $response): Letter + { + $letter = new Letter(); + + $letter->setJobId($response['letter']['job_id']); + $letter->setColor((int)$response['letter']['specification']['color'] === 4); + $letter->setDuplex($response['letter']['specification']['mode'] === 'duplex'); + $letter->setInternational($response['letter']['specification']['ship'] === 'international'); + $letter->setPages($response['letter']['specification']['page']); + $letter->setPrice($response['letter']['price']); + $letter->setStatus($response['letter']['status']); + + if (!empty($response['letter']['address'])) { + $letter->setAddress($response['letter']['address']); + } + + if (!empty($response['notice']['balance'])) { + $letter->setBalanceNotice($response['notice']['balance']); + } + + if (!empty($response['notice']['timer'])) { + $letter->setTimerNotice($response['notice']['timer']); + } + + return $letter; + } +}