PHP code example of setono / economic-php-sdk

1. Go to this page and download the library: Download setono/economic-php-sdk library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

setono / economic-php-sdk example snippets




use Setono\Economic\Client\Client;
use Setono\Economic\Exception\EconomicException;

ustomer = $client->customers()->getByNumber(5);
    if ($customer === null) {
        echo "Customer 5 not found.\n";
        return;
    }

    echo "Hello, {$customer->name} ({$customer->currency}).\n";
    echo "Balance: {$customer->balance}\n";
} catch (EconomicException $e) {
    // Any non-2xx from e-conomic; auth, validation, server errors.
    // $e->getMessage() 

$customer = $client->customers()->getByNumber(5);   // ?Customer
$product  = $client->products()->getByNumber('SKU-001'); // ?Product (product numbers are strings)
$order    = $client->orders()->drafts()->getByNumber(42); // ?Order
$invoice  = $client->invoices()->booked()->getByNumber(9001); // ?BookedInvoice

$customer->customerNumber;                       // ?int
$customer->name;                                 // ?string
$customer->email;                                // ?string
$customer->balance;                              // ?float (server-computed)
$customer->lastUpdated;                          // ?\DateTimeImmutable
$customer->customerGroup?->customerGroupNumber;  // ?int — typed reference DTO
$customer->salesPerson?->employeeNumber;         // ?int
$customer->paymentTerms?->self;                  // ?string — the reference's HATEOAS URL

// Link/meta fields (self, contacts, templates, totals, …) and future schema additions
// stay reachable via $raw:
$customer->raw['contacts'] ?? null;

use Setono\Economic\Client\Client;
use Setono\Economic\Request\CollectionRequestOptions;
use Setono\Economic\Request\Filter;

$client = new Client('demo', 'demo');

$page = $client->products()->getPage(
    new CollectionRequestOptions(
        pageSize: 50,
        filter:   Filter::like('name', 'b'),
        sortBy:   'name',
    ),
);

foreach ($page->collection as $product) {
    echo "{$product->productNumber}\t{$product->name}\n";
}

// $page also carries pagination metadata:
$page->pagination->results;             // int — items on this page
$page->pagination->resultsWithoutFilter; // int — items without the filter
$page->pagination->nextPage?->url;       // ?string — null on the last page

$opts = new CollectionRequestOptions(pageSize: 50);
$nameSorted = $opts->withSortBy('name');
$nameFiltered = $opts->withFilter(Filter::like('name', 'b'));

use Setono\Economic\Request\Filter;

// e.g. incremental sync: everything updated since the last run.
// Any \DateTimeInterface is converted to UTC and formatted for the wire — no
// manual ->setTimezone()/->format() needed:
$options = $options->withFilter(Filter::gte('lastUpdated', $lastSynchronization));

Filter::eq('email', null);                      // email$eq:$null:  (null = e-conomic's $null: sentinel)
Filter::like('city', '*port');                  // city$like:*port  (* wildcard; without it, "contains")
Filter::in('customerNumber', [2, 5, 7]);        // customerNumber$in:[2,5,7]  (max 200 elements)
Filter::eq('name', 'Joe')->and(                 // name$eq:Joe$and:(city$like:*port$or:age$lt:40)
    Filter::like('city', '*port')->or(Filter::lt('age', 40)),
);


use Setono\Economic\Client\Client;
use Setono\Economic\Request\CollectionRequestOptions;
use Setono\Economic\Request\Filter;

$client = new Client('demo', 'demo');

foreach ($client->products()->paginate() as $product) {
    // ... handle each product, one at a time, memory-flat
}

// With filter / sort:
foreach ($client->products()->paginate(new CollectionRequestOptions(filter: Filter::like('name', 'b'), sortBy: 'name')) as $product) {
    // ...
}

foreach ($client->customers()->paginate() as $customer)         { /* ... */ }
foreach ($client->orders()->drafts()->paginate() as $order)      { /* ... */ }
foreach ($client->orders()->sent()->paginate() as $order)        { /* ... */ }
foreach ($client->invoices()->booked()->paginate() as $invoice)  { /* ... */ }

$self = $client->self()->get();
// $self->raw contains the full /self response (loggedInUserType, serverTime, agreement details, …)

use Setono\Economic\Client\Client;
use Setono\Economic\Request\Identifier;
use Setono\Economic\Request\Order\DraftOrderRequest;
use Setono\Economic\Request\Order\Line;
use Setono\Economic\Request\Order\Notes;
use Setono\Economic\Request\Order\Recipient;

$client = new Client('API_KEY', 'API_SECRET');

$request = new DraftOrderRequest(
    date:         '2026-05-27',
    currency:     'DKK',
    layout:       Identifier::layout(17),
    paymentTerms: Identifier::paymentTerms(1),
    customer:     Identifier::customer(1),
    recipient:    new Recipient(name: 'Foo', vatZone: Identifier::vatZone(1)),
);

$order = $client->orders()->drafts()->create($request);
$order->orderNumber;       // typed (int)
$order->grossAmount ?? 0;  // server-computed fields are typed too (?float)

$request = new DraftOrderRequest(
    date:         '2026-05-27',
    currency:     'DKK',
    layout:       Identifier::layout(17),
    paymentTerms: Identifier::paymentTerms(1),
    customer:     Identifier::customer(1),
    recipient:    new Recipient(name: 'Foo', vatZone: Identifier::vatZone(1)),
    notes:        new Notes(heading: 'Greetings'),
    lines: [
        new Line(
            description:  'Widget',
            quantity:     2.5,
            unitNetPrice: 49.95,
            product:      Identifier::product('SKU-001'),
        ),
    ],
);

$order = $client->orders()->drafts()->getByNumber(42);

$request = DraftOrderRequest::fromResponse($order);
$request->notes = new Notes(heading: 'Updated');

$updated = $client->orders()->drafts()->update(42, $request);

use Setono\Economic\Client\Client;
use Setono\Economic\Request\Customer\CustomerRequest;
use Setono\Economic\Request\Identifier;

$client = new Client('API_KEY', 'API_SECRET');

$request = new CustomerRequest(
    name:          'Acme A/S',
    currency:      'DKK',
    customerGroup: Identifier::customerGroup(1),
    vatZone:       Identifier::vatZone(1),
    paymentTerms:  Identifier::paymentTerms(14),
);

$customer = $client->customers()->create($request);
$customer->customerNumber;                       // typed (int) — server-assigned
$customer->name;                                 // typed (string)
$customer->customerGroup?->customerGroupNumber;  // typed reference DTO

$request = new CustomerRequest(
    name:          'Acme A/S',
    currency:      'DKK',
    customerGroup: Identifier::customerGroup(1),
    vatZone:       Identifier::vatZone(1),
    paymentTerms:  Identifier::paymentTerms(14),
    email:         '[email protected]',
    address:       'Main 1',
    zip:           '2100',
    city:          'Copenhagen',
    country:       'Denmark',
    corporateIdentificationNumber: '12345678',
    layout:        Identifier::layout(17),
    salesPerson:   Identifier::employee(5),
);

$customer = $client->customers()->getByNumber(42);

$request = CustomerRequest::fromResponse($customer);
$request->email = '[email protected]';  // change what you need
$request->mobilePhone = null;              // null = omitted from the JSON = cleared server-side

$updated = $client->customers()->update(42, $request);

$product = $client->products()->getByNumber('does-not-exist');
if ($product === null) {
    // not found — no exception
}

use Setono\Economic\Exception\EconomicException;
use Setono\Economic\Exception\NotFoundException;
use Setono\Economic\Exception\ValidationException;

try {
    $customer = $client->customers()->create($req);
} catch (ValidationException $e) {
    // e-conomic's structured validation errors:
    $errors = $e->getValidationErrors(); // raw nested document; see e-conomic docs
    $hint   = $e->getDeveloperHint();
    $logId  = $e->getLogId();           // 

use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Response;
use Setono\Economic\Client\Client;

$fake = new class() implements ClientInterface {
    public ?RequestInterface $captured = null;

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $this->captured = $request;

        return new Response(
            200,
            ['Content-Type' => 'application/json'],
            '{"customerNumber":1,"name":"Acme","currency":"DKK"}',
        );
    }
};

$client = new Client('demo', 'demo', httpClient: $fake);
$customer = $client->customers()->getByNumber(1);

// Assert against the captured request:
assert($fake->captured?->getMethod() === 'GET');
assert((string) $fake->captured->getUri() === 'https://restapi.e-conomic.com/customers/1');

// And the typed response:
assert($customer?->customerNumber === 1);
assert($customer->name === 'Acme');

$customer = new \Setono\Economic\Response\Customer\Customer(
    customerNumber: 1,
    name:           'Acme',
    currency:       'DKK',
);
$customer->raw = ['extraField' => 'value']; // $raw is intentionally not readonly

$product = $client->products()->getByNumber('5');
$product->name;                  // typed
$product->costPrice;             // typed (?float)
$product->raw['self'] ?? null;   // link fields stay in $raw

file_put_contents($cachePath, json_encode($product->raw, JSON_THROW_ON_ERROR));

// ✗ antipattern — pays discovery + builder construction per item
foreach ($customerImports as $row) {
    $client = new Client($token, $agreement);            // expensive per iteration
    $client->customers()->create($row->toRequest());
}

// ✓ corrected — one Client, cached Valinor builders for batch workloads
$cache  = new \CuyZ\Valinor\Cache\FileSystemCache('/var/cache/economic');
$client = new Client(
    $token, $agreement,
    mapperBuilder:     Client::configureMapperBuilder(
        (new \CuyZ\Valinor\MapperBuilder())->withCache($cache),
    ),
    normalizerBuilder: Client::registerNormalizerTransformers(
        (new \CuyZ\Valinor\NormalizerBuilder())->withCache($cache),
    ),
);
foreach ($customerImports as $row) {
    $client->customers()->create($row->toRequest());
}

$client = new Setono\Economic\Client\Client('API_KEY', 'API_SECRET');

$data = $client->get('customers/123');                 // array<string, mixed>
$list = $client->get('customers', ['pagesize' => 10]); // array<string, mixed>
$page = $client->get('https://restapi.e-conomic.com/customers?skippages=2&pagesize=20');

$response = $client->request($request);   // PSR-7 ResponseInterface



use Setono\Economic\Client\Client;

R-18 client and PSR-17 factories that are installed.
$client = new Client('API_KEY', 'API_SECRET');



use Setono\Economic\Client\Client;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
use Symfony\Component\HttpClient\RetryableHttpClient;

$transport = new RetryableHttpClient(
    HttpClient::create(),
    new GenericRetryStrategy(
        // retry on these status codes, with default exponential backoff:
        statusCodes: [423, 425, 429, 500, 502, 503, 504, 507, 510],
        delayMs:     1_000,
        multiplier:  2.0,
        maxDelayMs:  10_000,
    ),
    maxRetries: 3,
);

$client = new Client('API_KEY', 'API_SECRET', httpClient: new Psr18Client($transport));



use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Setono\Economic\Client\Client;

final readonly class LoggingHttpClient implements ClientInterface
{
    public function __construct(
        private ClientInterface $inner,
        private LoggerInterface $logger,
    ) {}

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $start = microtime(true);
        try {
            $response = $this->inner->sendRequest($request);
            $this->logger->info('economic API call', [
                'method'      => $request->getMethod(),
                'uri'         => (string) $request->getUri()->withQuery('')->withFragment(''),
                'status'      => $response->getStatusCode(),
                'duration_ms' => (int) ((microtime(true) - $start) * 1000),
            ]);

            return $response;
        } catch (\Throwable $e) {
            $this->logger->error('economic API call failed', [
                'method'      => $request->getMethod(),
                'uri'         => (string) $request->getUri()->withQuery('')->withFragment(''),
                'exception'   => $e::class,
                'duration_ms' => (int) ((microtime(true) - $start) * 1000),
            ]);
            throw $e;
        }
    }
}

$psr18 = new \Symfony\Component\HttpClient\Psr18Client();
$client = new Client('API_KEY', 'API_SECRET', httpClient: new LoggingHttpClient($psr18, $logger));



use CuyZ\Valinor\Cache\FileSystemCache;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\NormalizerBuilder;
use Setono\Economic\Client\Client;

);
$normalizerBuilder = Client::registerNormalizerTransformers(
    (new NormalizerBuilder())->withCache($cache),
);

$client = new Client(
    'API_KEY',
    'API_SECRET',
    mapperBuilder:     $mapperBuilder,
    normalizerBuilder: $normalizerBuilder,
);

$cache = new FileSystemCache('/var/cache/economic');
if ($_ENV['APP_ENV'] === 'dev') {
    $cache = new \CuyZ\Valinor\Cache\FileWatchingCache($cache);
}
bash
composer