{"id":50891633,"url":"https://github.com/be-lenka/ship8-php-sdk","last_synced_at":"2026-06-15T21:30:42.226Z","repository":{"id":361179306,"uuid":"1228673425","full_name":"be-lenka/ship8-php-sdk","owner":"be-lenka","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-29T12:57:26.000Z","size":69,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-29T14:20:28.107Z","etag":null,"topics":["fulfillment","php-sdk","ship8"],"latest_commit_sha":null,"homepage":"https://portal.ship8.com/api-docs/index.html#section/Environment","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/be-lenka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-04T08:55:05.000Z","updated_at":"2026-05-29T12:57:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/be-lenka/ship8-php-sdk","commit_stats":null,"previous_names":["be-lenka/ship8-php-sdk"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/be-lenka/ship8-php-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/be-lenka%2Fship8-php-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/be-lenka%2Fship8-php-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/be-lenka%2Fship8-php-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/be-lenka%2Fship8-php-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/be-lenka","download_url":"https://codeload.github.com/be-lenka/ship8-php-sdk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/be-lenka%2Fship8-php-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34381757,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fulfillment","php-sdk","ship8"],"created_at":"2026-06-15T21:30:41.363Z","updated_at":"2026-06-15T21:30:42.214Z","avatar_url":"https://github.com/be-lenka.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ship8 — PHP SDK\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![PHP](https://img.shields.io/badge/PHP-7.4%20%7C%208.x-777BB4.svg)](https://www.php.net/)\n[![CI](https://github.com/be-lenka/ship8-php-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/be-lenka/ship8-php-sdk/actions/workflows/ci.yml)\n[![Code Style: PSR-12](https://img.shields.io/badge/Code%20Style-PSR--12-brightgreen.svg)](https://www.php-fig.org/psr/psr-12/)\n\nPHP client for the [Ship8](https://ship8.com) fulfillment / 3PL platform.\nProvides typed models, JWT authentication and Guzzle-based transport so you\ncan integrate orders, shipments, inbound POs, receiving, releases, products,\ninventory, invoices and freight quotes from any PHP application.\n\n\u003e **Status:** v0.2 — endpoints and models match the published Ship8 OpenAPI\n\u003e spec (see `resources/swagger.json`). Authenticated against the sandbox\n\u003e environment.\n\n## Installation \u0026 Usage\n\n### Requirements\n\n- PHP 7.4 or later (CI runs against 7.4, 8.0, 8.1, 8.2 and 8.3)\n- Composer 2.x\n- ext-curl, ext-json, ext-mbstring\n- Guzzle 6.2+ or 7.x (pulled in transitively via Composer)\n\n### Composer\n\n```json\n{\n  \"repositories\": [\n    {\n      \"type\": \"vcs\",\n      \"url\": \"https://github.com/be-lenka/ship8-php-sdk.git\"\n    }\n  ],\n  \"require\": {\n    \"be-lenka/ship8-php-sdk\": \"*@dev\"\n  }\n}\n```\n\nThen run `composer install`.\n\n## Hosts\n\nShip8 paths begin with `/api/app/...` so the host has no version prefix.\n\n| Environment                              | URL                          |\n|------------------------------------------|------------------------------|\n| `Configuration::ENV_PRODUCTION`          | `https://portal.ship8.com`   |\n| `Configuration::ENV_SANDBOX` *(default)* | `https://sandbox.ship8.com`  |\n\nSelect the target environment with `Configuration::setEnvironment()`:\n\n```php\n$config = new Configuration();\n$config-\u003esetEnvironment(Configuration::ENV_SANDBOX);\n// $config-\u003egetHost() === 'https://sandbox.ship8.com'\n```\n\n`setHost()` remains as an escape hatch for proxies, local mocks, or temporary\noverrides; it accepts any URL and bypasses the environment table.\n\n## Authentication\n\nShip8 uses a simplified JWT flow. Exchange your account email + password for\nan access token via `POST /api/app/account/requestToken`, then attach\n`Authorization: Bearer \u003ctoken\u003e` to every business call. The `Auth` helper\nencapsulates this and stores the resolved token on `Configuration` so any\n`Api` instance built from the same `Configuration` picks it up automatically.\n\n```php\n\u003c?php\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse BeLenka\\Ship8\\Auth;\nuse BeLenka\\Ship8\\Configuration;\nuse BeLenka\\Ship8\\Api\\OrderApi;\nuse BeLenka\\Ship8\\Model\\OrderCreationDto;\nuse BeLenka\\Ship8\\Model\\OrderItemCreationDto;\n\n// 1. Configure the client.\n$config = Configuration::getDefaultConfiguration()\n    -\u003esetEnvironment(Configuration::ENV_SANDBOX);\n\n// 2. Authenticate. Credentials should come from your app config — never\n// hard-code them.\n$auth = new Auth($email, $password, $config);\n$auth-\u003eauthenticate();\n\n// 3. Call the API.\n$orderApi = new OrderApi(null, $config);\n\n$order = (new OrderCreationDto())\n    -\u003esetCustomerCode('ACME')\n    -\u003esetCustomerOrderNo('SO-001')\n    -\u003esetCustomerOrderDate(new \\DateTime())\n    -\u003esetCarrierSCACCode('FDEG')\n    -\u003esetCrossDocking(false)\n    -\u003esetShipToLevel(OrderCreationDto::SHIP_TO_LEVEL_CUSTOMER)\n    -\u003esetShipToCustomerName('Alice Foo')\n    -\u003esetShipToAddressLine1('1 Main St')\n    -\u003esetShipToCity('Austin')\n    -\u003esetShipToState('TX')\n    -\u003esetShipToZipCode('78701')\n    -\u003esetShipToCountry('US')\n    -\u003esetOrderItems([\n        (new OrderItemCreationDto())-\u003esetItemNo('SKU-1')-\u003esetItemQty(5),\n    ]);\n\ntry {\n    $out = $orderApi-\u003ecreate($order);\n    printf(\"Created %s — status %s\\n\", $out-\u003egetOrderNo(), $out-\u003egetStatus());\n} catch (\\BeLenka\\Ship8\\ApiException $e) {\n    fwrite(STDERR, sprintf(\"Ship8 error [%d]: %s\\n\", $e-\u003egetCode(), $e-\u003egetMessage()));\n}\n```\n\nWhen the access token expires you can rotate it without re-prompting for the\npassword:\n\n```php\n$auth-\u003erefresh(); // uses the access + refresh tokens captured during authenticate()\n```\n\n## Response envelope\n\nShip8 wraps every response in a `ResultDto` envelope:\n\n```json\n{ \"successful\": true, \"code\": \"0\", \"message\": \"OK\", \"data\": { ... } }\n```\n\n`AbstractApi` unwraps this transparently: SDK callers receive the inner `data`\ndeserialized into the appropriate model. When `successful` is `false`, an\n`ApiException` is raised carrying the server's `code` and `message`.\n\n## Accessing model properties (do not use `method_exists`)\n\nModel getters and setters are dispatched through `AbstractModel::__call()` —\nthey are not declared as concrete methods. PHP's `method_exists()` only sees\nexplicitly declared methods, so a guard like:\n\n```php\n// BUG: always false → silently empty result\nif (method_exists($inv, 'getInventoryDetails')) {\n    $rows = $inv-\u003egetInventoryDetails();\n}\n```\n\nwill **never** enter the `if`. Use one of the supported patterns instead:\n\n```php\n// 1) Direct call — every API method declares its return type, just trust it\n$rows = $api-\u003egetInventory()-\u003egetInventoryDetails() ?? [];\n\n// 2) instanceof check (defensive against null / exception paths)\n$inv = $api-\u003egetInventory();\nif ($inv instanceof \\BeLenka\\Ship8\\Model\\InventoryDto) {\n    $rows = $inv-\u003egetInventoryDetails() ?? [];\n}\n\n// 3) hasProperty() helper (when the model class is not known statically)\nif ($model \u0026\u0026 $model-\u003ehasProperty('inventoryDetails')) {\n    $rows = $model-\u003egetInventoryDetails() ?? [];\n}\n\n// 4) is_callable also works (sees magic methods)\nif (is_callable([$inv, 'getInventoryDetails'])) {\n    $rows = $inv-\u003egetInventoryDetails() ?? [];\n}\n```\n\n## API endpoints\n\n| Class                       | Method                       | HTTP                                                              |\n|-----------------------------|------------------------------|-------------------------------------------------------------------|\n| `AccountApi`                | `requestToken`               | `POST /api/app/account/requestToken`                              |\n| `AccountApi`                | `refreshToken`               | `POST /api/app/account/refreshToken`                              |\n| `OrderApi`                  | `create`                     | `POST /api/app/order/create`                                      |\n| `OrderApi`                  | `get`                        | `GET  /api/app/order/get`                                         |\n| `ShipmentApi`               | `get`                        | `GET  /api/app/shipment/get`                                      |\n| `ProductApi`                | `upsert`                     | `POST /api/app/product/upsert`                                    |\n| `ProductApi`                | `getInventory`               | `GET  /api/app/product/getInventory`                              |\n| `InboundPOApi`              | `create`                     | `POST /api/app/inboundPO/create`                                  |\n| `InboundPOApi`              | `createEECBondedDC`          | `POST /api/app/inboundPO/createEECBondedDC`                       |\n| `ReceivingApi`              | `create`                     | `POST /api/app/receiving/create`                                  |\n| `ReleaseSOApi`              | `create`                     | `POST /api/app/releaseSO/create`                                  |\n| `InvoiceApi`                | `list`                       | `GET  /api/app/invoice/list`                                      |\n| `CompanyApi`                | `getBondedDCCompany`         | `GET  /api/app/company/getBondedDCCompany`                        |\n| `CustomerFreightQuoteApi`   | `getEstimatedShippingCost`   | `POST /api/app/customerFreightQuote/getEstimatedShippingCost`     |\n\n## Architecture\n\n```\nlib/\n├── Configuration.php       host, credentials, debug, user-agent\n├── Auth.php                Ship8 JWT (requestToken / refreshToken)\n├── ApiException.php        thrown for non-2xx / business-level errors\n├── HeaderSelector.php      Accept / Content-Type negotiation\n├── ObjectSerializer.php    model \u003c-\u003e JSON mapping, query helpers\n├── Api/\n│   ├── AbstractApi.php     transport + ResultDto unwrap\n│   ├── AccountApi.php\n│   ├── OrderApi.php\n│   ├── ShipmentApi.php\n│   ├── ProductApi.php\n│   ├── InboundPOApi.php\n│   ├── ReceivingApi.php\n│   ├── ReleaseSOApi.php\n│   ├── InvoiceApi.php\n│   ├── CompanyApi.php\n│   └── CustomerFreightQuoteApi.php\n└── Model/\n    ├── ModelInterface.php\n    ├── AbstractModel.php   shared property bag + ArrayAccess + JSON\n    │                       (auto getXxx/setXxx via __call against $openAPITypes)\n    └── *Dto.php            one-to-one with swagger schemas\n```\n\nEach operation is a thin wrapper on top of `AbstractApi::request()`; adding a\nnew endpoint is a one-method change.\n\n## Tests\n\n```bash\ncomposer install\n./vendor/bin/phpunit\n```\n\n## API Coverage\n\nThe SDK provides complete implementation coverage of the Ship8 OpenAPI specification. The swagger definition is sourced from https://portal.ship8.com/swagger/Public/swagger.json and stored locally in `resources/swagger.json` for reference.\n\n### Coverage Summary\n\n| Metric | Coverage |\n|--------|----------|\n| API Endpoints | 14/14 (100%) |\n| Request/Response Models | 23/23 (100%) |\n| Total Implemented Models | 40 |\n\n### Implemented Resources\n\n- **Account** (2 endpoints) — JWT token request \u0026 refresh\n- **Company** (1 endpoint) — Bonded DC company info\n- **Order** (2 endpoints) — Create \u0026 retrieve orders\n- **Shipment** (1 endpoint) — Retrieve shipment details\n- **Product** (2 endpoints) — Upsert products \u0026 query inventory\n- **InboundPO** (2 endpoints) — Create inbound POs (standard \u0026 EEC bonded DC)\n- **Receiving** (1 endpoint) — Create receiving orders\n- **ReleaseSO** (1 endpoint) — Create release SO\n- **Invoice** (1 endpoint) — List invoices\n- **CustomerFreightQuote** (1 endpoint) — Estimate shipping costs\n\nAll request/response data transfer objects are typed and match the Ship8 API specification.\n\n## License\n\nMIT — see `composer.json`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbe-lenka%2Fship8-php-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbe-lenka%2Fship8-php-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbe-lenka%2Fship8-php-sdk/lists"}