maxs94 / internetmarke-rest-php
Deutsche Post INTERNETMARKE REST client for PHP
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.12
- phpunit/phpunit: ^9.6
README
A modern PHP 8.1+ REST client for the Deutsche Post Internetmarke API. Create postage labels, access Portokasse, and integrate Internetmarke into your PHP applications.
Install
composer require maxs94/internetmarke-rest-php
Quick example
use Maxs94\Internetmarke\Internetmarke; use Maxs94\Internetmarke\Model\{Address, Position, ShoppingCartPDFRequest, ShoppingCartPosition}; $im = new Internetmarke('client-id', 'client-secret', 'username', 'password'); $app = $im->getAppResource(); $cart = $app->createShoppingCart(); $sender = (new Address())->setName('Max Mustermann')->setAddressLine1('Musterstraße 1') ->setPostalCode('94032')->setCity('Passau')->setCountry('DEU'); $receiver = (new Address())->setName('Erika Mustermann')->setAddressLine1('Beispielgasse 7') ->setPostalCode('10115')->setCity('Berlin')->setCountry('DEU'); $result = $app->checkoutShoppingCartAsPDF( (new ShoppingCartPDFRequest()) ->setShopOrderId($cart->getShopOrderId()) ->setTotal(95) // euro cents ->setPositions([ (new ShoppingCartPosition()) ->setProductCode(68) // Kompaktbrief ->setVoucherLayout(ShoppingCartPosition::VOUCHER_LAYOUT_ADDRESS_ZONE) ->setPosition(new Position(1, 1, 1)) ->setSender($sender) ->setReceiver($receiver), ]) ); file_put_contents('stamp.pdf', file_get_contents($result->getLink()));
See the examples/ directory for runnable scripts.
Features
| Feature | This library | Auto-generated SDK | SOAP libs |
|---|---|---|---|
| REST API | ✅ | ✅ | ❌ |
| PHP 8.1+ native | ✅ | ✅ | ✅ |
| Lightweight (1 dep) | ✅ | ❌ | ✅ |
| Typed DTOs | ✅ | ✅ | ✅ |
| Input validation | ✅ | ❌ | ❌ |
| Token auto-refresh | ✅ | ❌ | N/A |
| PDF labels | ✅ | ✅ | ✅ |
| PNG labels | ✅ | ✅ | ✅ |
| Portokasse wallet | ✅ | ✅ | ✅ |
| Retoure (refunds) | ✅ | ✅ | ✅ |
API coverage
| Endpoint | Method | PHP |
|---|---|---|
GET / |
Health check | ApiVersionResource::getVersion() |
POST /user |
Authenticate | handled internally by TokenProvider |
GET /user/profile |
User profile | UserResource::getUserProfile() |
PUT /app/wallet |
Charge wallet | AppResource::chargeWallet(int $amount) |
POST /app/shoppingcart |
Create cart | AppResource::createShoppingCart() |
GET /app/shoppingcart/{shopOrderId} |
Retrieve cart | AppResource::getShoppingCart(string $shopOrderId) |
POST /app/shoppingcart/pdf |
Checkout PDF | AppResource::checkoutShoppingCartAsPDF(...) |
POST /app/shoppingcart/png |
Checkout PNG | AppResource::checkoutShoppingCartAsPNG(...) |
GET /app/retoure |
Retoure state | AppResource::getRetoure(...) |
POST /app/retoure |
Request retoure | AppResource::setRetoure(RetoureVouchersRequest $r) |
GET /app/catalog |
Catalog | AppResource::getCatalog(array $types) |
Requirements
- PHP 8.1+
- Composer
- GuzzleHTTP 7+ (pulled in automatically)
Usage
Authentication
use Maxs94\Internetmarke\Internetmarke; $im = new Internetmarke( clientId: 'your-client-id', clientSecret: 'your-client-secret', username: 'your-username', password: 'your-password', ); // API version / health check $version = $im->getApiVersionResource()->getVersion(); echo $version->getVersion(); // e.g. v1.1.4 // User profile $profile = $im->getUserResource()->getUserProfile(); echo $profile->getFirstname() . ' ' . $profile->getLastname();
Creating a PDF label
use Maxs94\Internetmarke\Model\Address; use Maxs94\Internetmarke\Model\Position; use Maxs94\Internetmarke\Model\ShoppingCartPDFRequest; use Maxs94\Internetmarke\Model\ShoppingCartPosition; $app = $im->getAppResource(); // 1. Open a cart $cart = $app->createShoppingCart(); // 2. Build the request $request = (new ShoppingCartPDFRequest()) ->setShopOrderId($cart->getShopOrderId()) ->setTotal(95) // euro cents — must match position prices ->setPositions([ (new ShoppingCartPosition()) ->setProductCode(68) ->setVoucherLayout(ShoppingCartPosition::VOUCHER_LAYOUT_ADDRESS_ZONE) ->setPosition(new Position(1, 1, 1)) // x, y, page ->setSender($sender) ->setReceiver($receiver), ]); // 3. Checkout $result = $app->checkoutShoppingCartAsPDF($request); $pdfLink = $result->getLink(); // link to the generated PDF $manifestLink = $result->getManifestLink(); // posting receipt, if requested $walletLeft = $result->getWalletBallance(); // remaining balance in euro cents
For PNG labels, use ShoppingCartPNGRequest and checkoutShoppingCartAsPNG().
Both checkout methods accept optional flags:
$app->checkoutShoppingCartAsPDF($request, validate: true); // preview — no purchase $app->checkoutShoppingCartAsPDF($request, directCheckout: true); // skip cart step
Catalog
use Maxs94\Internetmarke\Model\RetrieveCatalogResponse; // motif images + product list $catalog = $app->getCatalog([RetrieveCatalogResponse::TYPE_PUBLIC]); // page formats (needed for PDF pageFormatId) $formats = $app->getCatalog([RetrieveCatalogResponse::TYPE_PAGE_FORMATS]); // both at once $all = $app->getCatalog([ RetrieveCatalogResponse::TYPE_PUBLIC, RetrieveCatalogResponse::TYPE_PAGE_FORMATS, ]);
Wallet
$app->chargeWallet(500); // charge 5.00 EUR (amount in euro cents, minimum 1)
Retoure
use Maxs94\Internetmarke\Model\RetoureVouchersRequest; use Maxs94\Internetmarke\Model\ShoppingCart; use Maxs94\Internetmarke\Model\Voucher; // query retoure state $state = $app->getRetoure( shopRetoureId: '12345', startDate: new \DateTimeImmutable('2024-01-01'), endDate: new \DateTimeImmutable('2024-12-31'), ); // request a refund $voucher = Voucher::fromArray(['voucherId' => 'A00123C039...']); $cart = (new ShoppingCart())->setShopOrderId('98276337')->setVoucherList([$voucher]); $response = $app->setRetoure((new RetoureVouchersRequest())->setShoppingCart($cart)); echo $response->getShopRetoureId();
Validation
All request-side models validate their fields against the OpenAPI spec constraints and throw \InvalidArgumentException on violation. Validation is eager — it fires in the setter, at the point of the bad assignment.
| Constraint | Examples |
|---|---|
| String length | Address::setPostalCode (exactly 5 chars), Address::setCountry (exactly 3), shopOrderId (1–18) |
| Integer minimum | chargeWallet $amount (≥1), ShoppingCartPosition::productCode (≥1), VoucherPosition coords (≥1) |
| Enum | ShoppingCartPosition::voucherLayout, dpi, createShippingList |
| Array min items | ShoppingCart::setVoucherList (≥1 item) |
Sandbox / custom base URI
use Maxs94\Internetmarke\Config\ClientConfig; $im = new Internetmarke( clientId: 'your-client-id', clientSecret: 'your-client-secret', username: 'your-username', password: 'your-password', config: new ClientConfig('https://api-eu.dhl.com/post/de/shipping/im/v1'), );
The default base URI points to production. Pass a ClientConfig with a different URL for sandbox testing.
Architecture
src/
├── Authentication/ TokenProvider — OAuth token lifecycle
├── Config/ ClientConfig — base URI
├── Exception/ ApiException
├── Http/ ApiClient, ApiClientInterface, Serializer
├── Model/ DTOs for all request/response payloads
├── Service/ ApiVersionResource, AppResource, UserResource
└── Validator/ StringLengthValidator, IntegerMinValidator, EnumValidator
TokenProvider and ApiClient implement interfaces (TokenProviderInterface, ApiClientInterface), making them easy to swap in tests or for custom HTTP adapters.
Development
composer install make phpunit # run tests make phpstan # static analysis make csfix # fix code style
Contributing
- Add tests for any new behaviour.
- Run
make phpunit,make phpstan, andmake csfixbefore opening a PR. - Keep backward-compatibility in mind; update docs when making breaking changes.
License
MIT — see LICENSE.