malpka32/inpost-buy-sdk

PHP client for InPost Buy (inpsa) API

Maintainers

Package info

github.com/malpka32/inpost-buy-sdk

pkg:composer/malpka32/inpost-buy-sdk

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.7.4 2026-03-26 02:22 UTC

This package is auto-updated.

Last update: 2026-03-26 02:22:55 UTC


README

PHP client for the InPost Buy API (inpsa) — sell your products through InPost's marketplace. Categories, offers, orders — all wrapped in a clean, type-safe interface.

CI PHP PHPStan Code style: PSR-12 Packagist Version Maintained License

What is InPost Buy?

InPost Buy (inpsa) lets merchants integrate their product catalog and orders with InPost's platform. You publish offers, receive orders, and update their status — all via REST API. This SDK handles authentication, serialization, and mapping so you focus on business logic.

Features

  • Categories — fetch product categories as a tree (read-only), with details and attributes
  • Accept-Language — set response language (Polish pl or English en) via Language enum
  • Offers — create single or batch offers with products, stock, and pricing; close/reopen; events; deposit types
  • Offer attachments — list, upload, download, delete attachments (images etc.)
  • Orders — list orders, fetch details, accept or refuse with status updates
  • OAuth2 — client credentials grant (default) with in-memory caching; OAuth2 PKCE (Authorization Code flow) for merchant integrations (e.g. PrestaShop modules)
  • Custom token provider — use InPostBuyClient::createWithTokenProvider() with any AccessTokenProviderInterface
  • Typed DTOsOfferDto, ProductDto, OrderDto etc., no raw arrays in your code
  • ExceptionsNotFoundException, BadRequestException, ServerException etc. with HTTP status and error details

Requirements

Installation

composer require malpka32/inpost-buy-sdk

Quick Start

<?php

use malpka32\InPostBuySdk\Client\InPostBuyClient;
use malpka32\InPostBuySdk\Dto\Common\ListSort;
use malpka32\InPostBuySdk\Dto\Offer\OfferStatus;
use malpka32\InPostBuySdk\Dto\Order\OrderStatus;
use Symfony\Component\HttpClient\HttpClient;

$client = new InPostBuyClient(
    httpClient: HttpClient::create(),
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    organizationId: 'your-org-uuid',
    sandbox: true,  // use false for production
);

// Fetch categories
$categories = $client->getCategories();
foreach ($categories as $category) {
    echo $category->name . " (" . $category->id . ")\n";
}

// Fetch offers
$offers = $client->getOffers(offerStatus: [OfferStatus::PUBLISHED], limit: 20);

// Fetch orders
$orders = $client->getOrders(status: OrderStatus::CREATED, sort: [ListSort::CREATED_AT_DESC]);

Usage

Language (Accept-Language)

API returns localized content (category names, error messages etc.). Set language via Language enum:

use malpka32\InPostBuySdk\Config\Language;

// Polish (default)
$client = new InPostBuyClient(..., language: Language::Polish);

// English
$client = InPostBuyClient::createWithTokenProvider(..., language: Language::English);

Supported values: Language::Polish (pl), Language::English (en).

Categories

Categories are returned as a tree (CategoryTreeCollection — each node has id, name, parentId, children).

$categories = $client->getCategories();

foreach ($categories as $node) {
    printf(
        "ID: %s | Name: %s | Parent: %s | Children: %d\n",
        $node->id,
        $node->name,
        $node->parentId ?? '-',
        count($node->children)
    );
}

You can iterate the tree recursively:

$tree = $client->getCategories();  // one API call, returns CategoryTreeCollection

foreach ($tree as $root) {
    echo $root->name . "\n";
    foreach ($root->children as $child) {
        echo "  " . $child->name . "\n";
    }
}

Creating an Offer

An offer is built from nested DTOs: ProductDto, StockDto, PriceDto. Optionally add attributes (e.g. color, size) and dimensions.

use malpka32\InPostBuySdk\Client\InPostBuyClient;
use malpka32\InPostBuySdk\Dto\Offer\OfferDto;
use malpka32\InPostBuySdk\Dto\Offer\PriceDto;
use malpka32\InPostBuySdk\Dto\Offer\Product\DimensionDto;
use malpka32\InPostBuySdk\Dto\Offer\Product\ProductDto;
use malpka32\InPostBuySdk\Dto\Offer\StockDto;
use malpka32\InPostBuySdk\Collection\AttributeValueCollection;
use malpka32\InPostBuySdk\Dto\Attribute\AttributeValueDto;

$product = new ProductDto(
    name: 'Cool T-Shirt',
    description: 'Comfortable cotton t-shirt in various sizes.',
    brand: 'MyBrand',
    categoryId: '67909821-cc25-45ec-80ce-5ac4f2f01032',  // from getCategories()
    sku: 'TSHIRT-001',
    ean: '5901234567890',
    attributes: AttributeValueCollection::fromAttributes(
        new AttributeValueDto('attr-color-uuid', ['Red'], 'en'),
        new AttributeValueDto('attr-size-uuid', ['M', 'L'])
    ),
    dimension: new DimensionDto(width: 200, height: 50, length: 300, weight: 200)  // mm, g
);

$offer = new OfferDto(
    externalId: 'SKU-TSHIRT-001',
    product: $product,
    stock: new StockDto(quantity: 10, unit: 'UNIT'),
    price: new PriceDto(amount: 99.99, currency: 'PLN', taxRateInfo: '23%')
);

$result = $client->putOffer($offer);
echo "Created offer ID: {$result->offerId}\n";

Batch Offers

Create multiple offers in one request:

use malpka32\InPostBuySdk\Collection\OfferCollection;

$offers = OfferCollection::fromOffers($offer1, $offer2, $offer3);
$ids = $client->putOffers($offers);

foreach ($ids as $id) {
    echo "Created: $id\n";
}

Listing and Filtering Offers

use malpka32\InPostBuySdk\Dto\Common\ListSort;
use malpka32\InPostBuySdk\Dto\Offer\OfferStatus;

$offers = $client->getOffers(
    offerStatus: [OfferStatus::PENDING, OfferStatus::PUBLISHED],
    limit: 50,
    offset: 0,
    sort: [ListSort::UPDATED_AT_DESC]
);

foreach ($offers as $offer) {
    echo $offer->externalId . "" . $offer->product->name . "\n";
}

OAuth2 PKCE (merchant flow)

For integrations where merchants authorize via OAuth2 (e.g. PrestaShop modules), use PKCE flow:

use malpka32\InPostBuySdk\Auth\PkceOAuth2Client;
use malpka32\InPostBuySdk\Auth\PkceTokenProvider;
use malpka32\InPostBuySdk\Client\InPostBuyClient;
use malpka32\InPostBuySdk\Config\InPostBuyEndpoints;
use malpka32\InPostBuySdk\Config\Language;

// 1. Initiate authorization – redirect merchant to $result['authorize_url']
$pkceClient = new PkceOAuth2Client($httpClient);
$result = $pkceClient->initiateAuthorization(
    redirectUri: 'https://your-shop.com/module/callback',
    clientId: $clientId,
    sandbox: true,
    stateStorage: $yourPkceStateStorage,  // implement PkceStateStorageInterface
);

// 2. On callback – exchange code for tokens
$tokens = $pkceClient->exchangeCodeForTokens(
    code: $_GET['code'],
    redirectUri: $redirectUri,
    clientId: $clientId,
    clientSecret: $clientSecret,
    state: $_GET['state'],
    tokenUrl: InPostBuyEndpoints::tokenUrl($sandbox),
    stateStorage: $yourPkceStateStorage,
    tokenStorage: $yourTokenStorage,  // implement TokenStorageInterface
);

// 3. Create client with token provider
$tokenProvider = new PkceTokenProvider(
    $yourTokenStorage,
    $pkceClient,
    $clientId,
    $clientSecret,
    InPostBuyEndpoints::tokenUrl($sandbox),
);

$client = InPostBuyClient::createWithTokenProvider(
    $httpClient,
    $tokenProvider,
    $organizationId,
    sandbox: true,
    language: Language::English,  // optional: pl (default) or en
);

Orders

use malpka32\InPostBuySdk\Dto\Common\ListSort;
use malpka32\InPostBuySdk\Dto\Order\OrderPaymentStatus;
use malpka32\InPostBuySdk\Dto\Order\OrderStatus;
use malpka32\InPostBuySdk\Dto\Order\OrderStatusDto;
use malpka32\InPostBuySdk\Dto\Order\OrderUpdateStatus;

// List orders (optionally filter by status/payment status/sort)
$orders = $client->getOrders(
    status: OrderStatus::CREATED,
    paymentStatus: OrderPaymentStatus::PAID,
    sort: [ListSort::CREATED_AT_DESC],
);

foreach ($orders as $order) {
    echo $order->inpostOrderId . "" . ($order->reference ?? 'no ref') . "\n";
}

// Fetch single order
$order = $client->getOrder('order-uuid-from-inpost');
if ($order !== null) {
    var_dump($order->status, $order->orderLines);
}

// Accept order
$client->updateOrderStatus('order-uuid', new OrderStatusDto(status: OrderUpdateStatus::ACCEPTED));

// Refuse with reason
$client->updateOrderStatus('order-uuid', new OrderStatusDto(
    status: OrderUpdateStatus::REFUSED,
    comment: 'Out of stock'
));

Error Handling

The SDK throws specific exceptions for HTTP errors:

Exception HTTP
BadRequestException 400
UnauthorizedException 401
ForbiddenException 403
NotFoundException 404
UnprocessableEntityException 422
TooManyRequestsException 429
ServerException 5xx

All extend malpka32\InPostBuySdk\Exception\ApiException and provide:

  • getStatusCode() — HTTP status code
  • getResponseBody() — raw response body
  • getErrorResponse() — parsed error (errorCode, errorMessage, details)
  • isRetryable() — true for 5xx and 429
  • getRetryAfterSeconds() — from Retry-After header when available
use malpka32\InPostBuySdk\Exception\NotFoundException;
use malpka32\InPostBuySdk\Exception\ApiException;

try {
    $order = $client->getOrder('non-existent');
} catch (NotFoundException $e) {
    echo "Order not found: " . $e->getMessage();
} catch (ApiException $e) {
    echo "API error: " . $e->getStatusCode();
    if ($e->isRetryable()) {
        echo " – retry after " . ($e->getRetryAfterSeconds() ?? '?') . " seconds";
    }
}

Testing & quality

composer ci                # full check: PHPStan, cs-check, tests
composer test              # run tests
composer test:coverage     # tests + coverage (clover + HTML in build/coverage/)
composer phpstan           # static analysis (level 10)
composer cs-check          # code style check (PSR-12, dry run)
composer cs-fix            # fix code style (PHP-CS-Fixer)

With Docker:

docker compose run --rm test ci        # full check (recommended before commit)
docker compose run --rm test           # tests
docker compose run --rm test phpstan   # PHPStan
docker compose run --rm test cs-check  # code style

CI runs on push/PR (PHP 8.1–8.3): code style (PSR-12), PHPStan, tests with coverage. See Actions.

Documentation

Official InPost Buy (inpsa) API docs: inpsa-api-portal.inpost-group.com

Support the project

This project is actively maintained: we keep it in sync with InPost API changes and welcome issues and pull requests on GitHub.

If this library helps you, consider buying me a coffee — it allows me to maintain and update the library alongside InPost API changes.

buycoffee.to/malpka32

License

MIT