PHPackages                             marcincook/laravel-renteon-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. [API Development](/categories/api)
4. /
5. marcincook/laravel-renteon-api-client

ActiveLibrary[API Development](/categories/api)

marcincook/laravel-renteon-api-client
=====================================

Laravel client for the Renteon REST API (multi-country, manager-style facade).

v0.1.3(1mo ago)1160MITPHPPHP ^8.3

Since May 15Pushed 1mo agoCompare

[ Source](https://github.com/marcincook/laravel-renteon-api-client)[ Packagist](https://packagist.org/packages/marcincook/laravel-renteon-api-client)[ Docs](https://github.com/marcincook/laravel-renteon-api-client)[ RSS](/packages/marcincook-laravel-renteon-api-client/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (18)Versions (5)Used By (0)

Laravel Renteon API Client
==========================

[](#laravel-renteon-api-client)

A Laravel client for the [Renteon](https://renteon.com) REST API, with first-class support for multi-country setups (PL / ES / LT / …) and a manager-style facade familiar to Laravel developers.

> Official Renteon API reference:

Status
------

[](#status)

🚧 Early development — the API surface may still change before v1.0.0.

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 11, 12 or 13

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

[](#installation)

```
composer require marcincook/laravel-renteon-api-client
```

Publish the config:

```
php artisan vendor:publish --tag=renteon-config
```

Configuration
-------------

[](#configuration)

Set environment variables for each country you operate in:

```
RENTEON_DEFAULT_COUNTRY=pl

RENTEON_PL_API_ENABLED=true
RENTEON_PL_API_BASE_URL=https://your-tenant.renteon.com
RENTEON_PL_API_USERNAME=...
RENTEON_PL_API_PASSWORD=...
RENTEON_PL_API_CLIENT_ID=...
RENTEON_PL_API_SECRET=...
RENTEON_PL_API_SALT=00000000

# Optional — used only by reports (RealizationByOffice et al.)
RENTEON_REPORT_TOKEN=
RENTEON_REPORT_USERNAME=
RENTEON_REPORT_PASSWORD=
RENTEON_REPORT_OFFICE_ID=
```

The same set of variables is available for `RENTEON_ES_*` and `RENTEON_LT_*`. Authentication is performed transparently: the first call exchanges your credentials for an access token (`POST /token` with a Base64(SHA512(…)) signature), then re-uses it for the rest of the request lifecycle.

Quick start
-----------

[](#quick-start)

```
use MarcinCook\RenteonApi\RenteonManager;

$renteon = app(RenteonManager::class);

// Default country — config('renteon.default')
$offices = $renteon->offices()->getOffices();

// Explicit country
$cars = $renteon->for('es')->cars()->getAllCars(['IsActive' => true]);
```

You can also use the `Renteon` facade:

```
use Renteon;

$countries = Renteon::for('pl')->countries()->getCountries();
```

Resources
---------

[](#resources)

Each resource maps to a slice of the Renteon REST API. Method signatures are kept close to the upstream model names so the [official reference](https://demo.s2.renteon.com/en/Api/Help/Referenceex) is your primary documentation.

ResourceKey methods`offices()``getOffices(bool $onlyActive = true)`, `fetchOfficesFromApi()`, `clearCache()``countries()``getCountries()` (projected to `{id, name}`), `clearCache()``cars()``searchCars($filters)`, `getAllCars($filters)`, `getCar($id)``carCategories()``searchCarCategories($filters)`, `getCarCategory($id)``services()``searchServices()` — read-only service catalogue (`GET /api/ExService/Search`). Renteon has no create/update endpoint for services (`POST/PUT /api/ExService` → 404).`pricelists()``getPricelists()` — list of pricelists (`GET /api/ExPricelist`: `Id, Code, Name, Currency, …`)`addressBook()``searchAddressBook($filters)`, `findByEmail($email)`, `getAddressBook($id)`, `createAddressBook($payload)`, `updateAddressBook($id, $payload)``bookings()``availability(...)`, `create(...)`, `calculate(...)`, `save(...)`, `getByNumber($number)`, `cancel($number)`, `checkOut($number, $officeCode)`, `searchBookings(...)`, `getBookingsForMonth($y, $m)`, `getBookingById($id)``carActivities()``searchCarActivities($filters)`, `getCarActivityDefinitions()``finance()``searchFinanceDocuments($filters)`, `searchExportInvoices($filters)`, `findExportInvoiceByInvoiceId($id)`, `getCachedExPaymentTypes()`, `getCachedExportInvoiceDocumentTypes()``reports()``getRealizationByOffice($params)` — uses the separate report-token flowBooking flow example
--------------------

[](#booking-flow-example)

The typical "public-facing booking site → Renteon" pipeline looks like this:

```
use MarcinCook\RenteonApi\RenteonManager;

$renteon = app(RenteonManager::class)->for('pl');

// 1) Lookup dictionaries for the storefront (cache these).
$offices    = $renteon->offices()->getOffices();
$countries  = $renteon->countries()->getCountries();
$categories = $renteon->carCategories()->searchCarCategories(['IsActive' => true]);

// 2) Identify the customer (email-based lookup).
$customer = $renteon->addressBook()->findByEmail('jane@example.com', [
    // Optional pre-filter — keeps the search payload small.
    'AddressBookTypeIds' => [1],
    'IsActive' => true,
]);

if ($customer === null) {
    $customer = $renteon->addressBook()->createAddressBook([
        'Name'              => 'Jane Doe',
        'AddressBookTypeId' => 1,
        'Email'             => 'jane@example.com',
        'IsAgency'          => false,
        'IsAgent'           => false,
        'IsArtisan'         => false,
        'IsEmployee'        => false,
        'IsForeigner'       => false,
        'IsVatPayer'        => false,
        'AllowB2CAccess'    => true,
        'BillingDelayInDays' => 0,
        'SurveyDoNotEmail'  => false,
        'AddressBookPhones' => [
            ['Number' => '+48123456789', 'PhoneTypeId' => 1],
        ],
    ]);
}

// 3) Check availability for the chosen date window + office pair.
$availability = $renteon->bookings()->availability([
    'OfficeOutId'    => 1,
    'OfficeInId'     => 1,
    'DateTimeOut'    => '2026-06-10T10:00:00+02:00',
    'DateTimeIn'     => '2026-06-15T10:00:00+02:00',
    'AvailableOnly'  => true,
    'CarCategoryIds' => [/* optionally narrow down */],
]);

$pickedCategory = $availability[0]['AvailabilityCarCategories'][0] ?? null;

// 4) Create the booking (server computes prices + mandatory additions).
$draft = $renteon->bookings()->create([
    'OfficeOutId'             => 1,
    'OfficeInId'              => 1,
    'DateTimeOut'             => '2026-06-10T10:00:00+02:00',
    'DateTimeIn'              => '2026-06-15T10:00:00+02:00',
    'ClientId'                => $customer['Id'],
    'BookingTypeId'           => 1,
    'CurrencyId'              => 1,
    'AvailabilityCarCategory' => $pickedCategory,
]);

// (optional) Recalculate after the customer adds extras / insurance.
$draft['Services'][] = ['ServiceId' => 7, 'Quantity' => 1];
$draft = $renteon->bookings()->calculate($draft);

// 5) After your payment gateway clears the deposit & extras — persist it.
$booking = $renteon->bookings()->save(array_merge($draft, [
    'CarId'         => 42,
    'ClientId'      => $customer['Id'],
    'ClientName'    => 'Jane Doe',
    'ClientEmail'   => 'jane@example.com',
    'TotalDeposit'  => 365.38,
    'Remark'        => 'Paid online via TPay',
]));

// $booking['Number'] is now safe to persist in your local DB for the
// customer's booking history.
```

### Verified gotchas (production, confirmed with Renteon)

[](#verified-gotchas-production-confirmed-with-renteon)

Hard-won lessons from a live integration — get these wrong and `ExBooking/Save`returns an opaque `500 "Nullable object must have a value"`:

- **`BookingTypeId` must be `2` (Reservation), not `1` (Contract)** for a web booking. Type 1 is the single biggest cause of the 500. Pair with `BookingSubTypeId = 6` (Reservation).
- **Dates must be UTC Zulu** (`2026-06-12T08:00:00Z`). An explicit local offset (`+02:00`) trips a .NET `DateTime.Kind` conversion error on Create and Save.
- **`Save` requires at least one `Booking_Drivers` entry** — without it the server dereferences a null (500). A minimal driver `{AddressBookId, SortOrder, IsDriverLicenceDomestic:false, IsPassportDomestic:false, IsIdCardDomestic:false}`is enough; the client must have an address book address.
- **`Create` requires a `PricelistId`** on the `AvailabilityCarCategory`. You may override the amounts (`Amount`/`Total`/`TotalDeposit`) with your own prices, but the category must reference an existing Renteon pricelist.
- **The client needs ≥1 `PersonIdentification`** (e.g. type 3 = Driver license), or Calculate/Save return 422.
- **The API user must be a member of the booking's offices** (Renteon panel → user → "Offices") or Availability returns 0 categories.
- **The `/token` request expects `office_id` (snake\_case)** — non-"External" users require it; the External flag is mandatory for machine auth (otherwise a new-device email verification kicks in).

> Scope note: this `ExBooking/*` (External) API is fine when the CMS owns pricing and overrides amounts. For a Renteon-priced, multi-instance storefront, Renteon recommends the **Aggregator API** (`aggregator.renteon.com`, HTTP Basic) instead.

### Customer booking history

[](#customer-booking-history)

`getBookingsByClient()` returns a customer's full history. `ClientId` is the only search filter that actually narrows to one customer (`ClientIds`/`AddressBookId`are ignored). The rich export projection (Number, dates, office, car category, Total/TotalDeposit/TotalDomestic, BookingSubTypeCode, IsCancelled, Client\*) is enough to render a "my bookings" view without a per-booking call.

```
$history = $renteon->bookings()->getBookingsByClient($clientId);
```

> **Email lookup caveat:** `AddressBook::findByEmail()` is unreliable — Renteon has no email search filter, so it pages the address book and matches client-side and can miss the customer on a large base. **Store the Renteon `ClientId` locally**(per customer) when you create the booking, and look up history by that id.

Reports
-------

[](#reports)

`Reports::getRealizationByOffice()` uses a different authentication flow — set either `RENTEON_REPORT_TOKEN` (a raw bearer token captured from the Renteon panel) or `RENTEON_REPORT_USERNAME`/`RENTEON_REPORT_PASSWORD`/`RENTEON_REPORT_OFFICE_ID`. If neither is set, the client falls back to the country token.

```
$rows = $renteon->reports()->getRealizationByOffice([
    'DateTimeInFrom'           => '01.06.2026.',
    'DateTimeInTo'             => '30.06.2026.',
    'IncludeExternalDocuments' => false,
]);
```

Caching
-------

[](#caching)

Dictionary endpoints (offices, countries, payment types, document types) are cached using the Laravel cache repository, keyed per country. TTL defaults to 24h and is configurable via `RENTEON_DICT_CACHE_TTL` (seconds). Use the `clearCache()` method on each resource to invalidate.

Error handling
--------------

[](#error-handling)

All HTTP failures bubble up as exceptions:

- `MarcinCook\RenteonApi\Exceptions\RenteonAuthException` — `/token` endpoint failed or returned no `access_token`.
- `MarcinCook\RenteonApi\Exceptions\RenteonHttpException` — any other non-2xx response. The original `Illuminate\Http\Client\Response` is attached as `->response`.
- `MarcinCook\RenteonApi\Exceptions\RenteonException` — base class for both.

The two top-level dictionary getters (`getOffices`, `getCountries`) deliberately swallow API failures and log a warning — they're used in dropdowns where a transient outage shouldn't break the page.

Testing
-------

[](#testing)

```
vendor/bin/pest
vendor/bin/pint --test
```

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance94

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Every ~12 days

Total

4

Last Release

31d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4801df0d7dd5eb9a0bb0d7bc0b9d2aa5d648b1bfe9f76e8b9757c2615c035e56?d=identicon)[marcincook](/maintainers/marcincook)

---

Top Contributors

[![marcincook](https://avatars.githubusercontent.com/u/859167?v=4)](https://github.com/marcincook "marcincook (6 commits)")

---

Tags

laravel-php-api-client-renteon-car-rentalapilaravelrest-clientcar-rentalrenteon

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/marcincook-laravel-renteon-api-client/health.svg)

```
[![Health](https://phpackages.com/badges/marcincook-laravel-renteon-api-client/health.svg)](https://phpackages.com/packages/marcincook-laravel-renteon-api-client)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M133](/packages/roots-acorn)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[propaganistas/laravel-disposable-email

Disposable email validator

6023.0M7](/packages/propaganistas-laravel-disposable-email)

PHPackages © 2026

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