PHPackages                             arturnawrot/4over-api-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. arturnawrot/4over-api-client

ActiveLibrary

arturnawrot/4over-api-client
============================

PHP api client for 4over

101PHP

Since Apr 1Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/arturnawrot/4over-api-php)[ Packagist](https://packagist.org/packages/arturnawrot/4over-api-client)[ RSS](/packages/arturnawrot-4over-api-client/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

4over-api-php
=============

[](#4over-api-php)

A PHP client library for the [4over API](https://api-users.4over.com/). Built around a clean, extensible service pattern - adding new endpoints is straightforward and consistent.

> **Note:** Not all 4over API endpoints might be implemented. See [Available Services](#available-services) for what's ready, and [Adding a New Service](#adding-a-new-service) for how to contribute the rest (very easy).

---

Installation
------------

[](#installation)

```
composer require arturnawrot/4over-api-client
```

---

Quick Start
-----------

[](#quick-start)

```
use FourOver\FourOverApiClient;

$client = new FourOverApiClient('PUBLIC_KEY', 'PRIVATE_KEY', 'SANDBOX');

$categories = $client->categories->getAllCategories();
```

Services are accessed as properties on the client. Pass `'LIVE'` instead of `'SANDBOX'` when you're ready to hit the real API.

---

Available Services
------------------

[](#available-services)

PropertyService classMethods`$client->categories``CategoryService``getAllCategories()``$client->products``ProductService``getAllProducts()`, `getProductsByCategory($uuid)`, `getProduct($uuid)``$client->shipping``ShippingService``getShippingQuote(...)``$client->orders``OrderService``createOrder(...)`, `getOrderStatus($jobUuid)`, `getTrackingNumber($jobUuid)``$client->files``FileService``createFile($url)``$client->paymentProfiles``PaymentProfileService``getPaymentProfiles()`---

Usage Examples
--------------

[](#usage-examples)

### Get all categories

[](#get-all-categories)

```
$categories = $client->categories->getAllCategories();

foreach ($categories as $category) {
    echo $category->getCategoryName();
}
```

### Browse products

[](#browse-products)

```
// All products
$products = $client->products->getAllProducts();

// Products in a category
$products = $client->products->getProductsByCategory($categoryUuid);

// Single product with full option groups
$product = $client->products->getProduct($productUuid);
$optionGroups = $product->getProductOptionGroups();

$turnaroundOptions = $optionGroups->getTurnaroundOptionGroup()->getOptions();
$runsizeUuid    = $turnaroundOptions[0]->getRunsizeUuid();
$colorspecUuid  = $turnaroundOptions[0]->getColorspecUuid();
$turnaroundUuid = $turnaroundOptions[0]->getOptionUuid();
```

### Get a shipping quote

[](#get-a-shipping-quote)

```
$quote = $client->shipping->getShippingQuote(
    $productUuid,
    $runsizeUuid,
    $turnaroundUuid,
    $colorspecUuid,
    '4301 Washington Road.',
    '',
    'Evans',
    'GA',
    'US',
    '30809',
    $sets = 1
);

$option = $quote->getShippingOptions()[0];
echo $option->getServiceName();   // "FedEx Ground"
echo $option->getServicePrice();  // 12.50
echo $option->getServiceCode();
```

### Create an order

[](#create-an-order)

See [`examples/createOrder.php`](examples/createOrder.php) for a full working example. The high-level flow is:

```
// 1. Upload your print files
$front = $client->files->createFile('https://...dummy.pdf')->toArray();
$back  = $client->files->createFile('https://...dummy.pdf')->toArray();

$files = [
    'set_001' => [
        'job_name' => 'job001-001',
        'files'    => [
            'fr' => $front['files'][0]['file_uuid'],
            'bk' => $back['files'][0]['file_uuid'],
        ]
    ]
];

// 2. Create the order
$response = $client->orders->createOrder(
    $orderId, $couponCode, $skipConfirmation,
    $sets, $productUuid, $runsizeUuid, $turnaroundUuid, $colorspecUuid,
    $optionUuids, $dropship, $files,
    // ship_to fields...
    // ship_from fields...
    $shippingMethod, $shippingCode
);

$jobUuid = $response->getFirstJobUuid();
```

> In sandbox mode, a dummy payment profile token (`1010101010`) is used automatically if you don't provide one.

### Track an order

[](#track-an-order)

```
// Order status
$statusList = $client->orders->getOrderStatus($jobUuid);
$latest = $statusList->getLatestStatus();
echo $latest->getStatus();   // "In Production"
echo $latest->getDateSet();

// Tracking number (only available once shipped)
$tracking = $client->orders->getTrackingNumber($jobUuid);
echo $tracking->getTrackingNumber();
```

### Payment profiles

[](#payment-profiles)

```
$profiles = $client->paymentProfiles->getPaymentProfiles();

foreach ($profiles as $profile) {
    echo $profile->getLastFour();       // "4242"
    echo $profile->getType();           // "Visa"
    echo $profile->getProfileToken();
}
```

---

Caching (optional)
------------------

[](#caching-optional)

The client accepts any PSR-compatible HTTP client. A WordPress object cache integration is included out of the box:

```
use FourOver\FourOverApiClient;
use FourOver\HttpClients\WordpressCacheHttpClientFactory;

$client = new FourOverApiClient('PUBLIC_KEY', 'PRIVATE_KEY', 'LIVE');
$client->setHttpClient(WordpressCacheHttpClientFactory::get());
```

You can pass any other PSR-compatible HTTP client the same way - useful for adding custom caching, logging, or retry middleware via [Guzzle's handler stack](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html).

---

Adding a New Service
--------------------

[](#adding-a-new-service)

Every service follows the same pattern. Here's how to add one.

### 1. Create the entity

[](#1-create-the-entity)

```
// src/Entities/MyEntity.php

namespace FourOver\Entities;

use FourOver\Entities\BaseEntity;

class MyEntity extends BaseEntity
{
    private string $uuid;
    private string $name;

    public function getUuid() : string { return $this->uuid; }
    public function getName() : string { return $this->name; }
}
```

### 2. Create the entity list

[](#2-create-the-entity-list)

PHP didn't support generics at the time this library was written, so typed collections are implemented manually. Each list class extends `BaseList` (which itself extends `\ArrayObject`) and declares the type it holds via `getType()`. This gives you an iterable collection with a guaranteed element type - the same guarantee that generics provide in languages like Java or C#.

```
// src/Entities/MyEntityList.php

namespace FourOver\Entities;

use FourOver\Entities\BaseList;

class MyEntityList extends BaseList
{
    public static function getType() : string
    {
        return MyEntity::class;
    }
}
```

### 3. Create the service

[](#3-create-the-service)

```
// src/Services/MyNewService.php

namespace FourOver\Services;

use FourOver\Entities\MyEntity;
use FourOver\Entities\MyEntityList;

class MyNewService extends AbstractService
{
    public function getAll() : MyEntityList
    {
        return $this->getResource(
            'GET',
            '/my-endpoint',
            ['query' => ['max' => 100]],
            MyEntity::class,
            MyEntityList::class  // omit this for single-entity responses
        );
    }

    public function getOne(string $uuid) : MyEntity
    {
        return $this->getResource('GET', "/my-endpoint/{$uuid}", [], MyEntity::class);
    }

    public function create(string $name) : MyEntity
    {
        return $this->getResource(
            'POST',
            '/my-endpoint',
            ['body' => json_encode(['name' => $name])],
            MyEntity::class
        );
    }
}
```

### 4. Register the service

[](#4-register-the-service)

Add it to the class map in `src/Services/ServiceFactory.php`:

```
private static $classMap = [
    // ... existing entries
    'myNew' => MyNewService::class,
];
```

Your service is now available as `$client->myNew->getAll()`.

### How the service layer works

[](#how-the-service-layer-works)

When you call `$client->categories->getAllCategories()`:

1. `__get('categories')` on `FourOverApiClient` looks up `'categories'` in `ServiceFactory`'s class map and instantiates `CategoryService` with the client.
2. The service calls `getResource()`, which issues the HTTP request via the base client, passing the method, path, and any Guzzle options (query string, JSON body, etc.).
3. The base client builds the full URI and attaches API credentials in both places required by the 4over docs - as URL query parameters and as an `Authorization` header - before sending the request through the PSR HTTP client.
4. The response JSON is cleaned up: URLs are stripped from the payload (they confuse the mapper), and wrapper keys like `entities` or `job` are unwrapped to expose the actual data.
5. The cleaned array is handed to `JsonMapper`, which hydrates it into your entity class. For list responses, `mapArray()` is used and the result is placed into the typed list class. For single-entity responses, `map()` is used directly. Either way you get a fully typed return value.

---

Running the examples
--------------------

[](#running-the-examples)

```
composer install
php examples/getAllCategories.php
php examples/getShippingQuote.php
php examples/createOrder.php
```

---

License
-------

[](#license)

The MIT License (MIT). See [LICENSE.md](LICENSE.md) for details.

###  Health Score

21

—

LowBetter than 19% of packages

Maintenance64

Regular maintenance activity

Popularity3

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/c45fec95f323afdb52d2f961994c315f4239ded1e696f904ce059278cf76bec9?d=identicon)[arturnawrot](/maintainers/arturnawrot)

---

Top Contributors

[![arturnawrot](https://avatars.githubusercontent.com/u/32748505?v=4)](https://github.com/arturnawrot "arturnawrot (34 commits)")

### Embed Badge

![Health badge](/badges/arturnawrot-4over-api-client/health.svg)

```
[![Health](https://phpackages.com/badges/arturnawrot-4over-api-client/health.svg)](https://phpackages.com/packages/arturnawrot-4over-api-client)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
