PHPackages                             bluerock/superpdp-php-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. bluerock/superpdp-php-client

ActiveLibrary[API Development](/categories/api)

bluerock/superpdp-php-client
============================

The Official Super PDP API PHP Client/SDK

00PHP

Since May 26Pushed 2w agoCompare

[ Source](https://github.com/bluerocktel/SuperPDP-PHP-Api-Client)[ Packagist](https://packagist.org/packages/bluerock/superpdp-php-client)[ RSS](/packages/bluerock-superpdp-php-client/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

superpdp-php-client
===================

[](#superpdp-php-client)

[![Software License](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Latest Version on Packagist](https://camo.githubusercontent.com/4c9d4defa1e4f31f1882102718bebbd0109c16c1409c9fb8a5c3c67ee5294474/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f626c7565726f636b2f73757065727064702d7068702d636c69656e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bluerock/superpdp-php-client)[![Total Downloads](https://camo.githubusercontent.com/ac793dbb1158e32f0f950b31882d17fa5163ae50cc75c7376cfad59b4b1a58b4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626c7565726f636b2f73757065727064702d7068702d636c69656e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bluerock/superpdp-php-client)

PHP client/SDK for the [Super PDP API](https://api.superpdp.tech) — the platform for sending and receiving electronic invoices in compliance with France's e-invoicing reform and the Peppol network.

Built on [Saloon PHP](https://docs.saloon.dev/).

- [Installation](#installation)
- [Authentication](#authentication)
- [Usage](#usage)
    - [Resources](#resources)
    - [Companies](#companies)
    - [Sessions](#sessions)
    - [Invoices](#invoices)
    - [Invoice Events](#invoice-events)
    - [Directory Entries](#directory-entries)
    - [French Directory](#french-directory)
- [Entities (DTOs)](#entities)
- [Responses](#responses)
- [Testing](#testing)

---

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

[](#installation)

Requires PHP `^8.2`.

```
composer require bluerock/superpdp-php-client
```

---

Authentication
--------------

[](#authentication)

Super PDP uses **OAuth2**. Two authentication modes are available:

### Mode 1 — Simple bearer token

[](#mode-1--simple-bearer-token)

If you already have a valid access token (e.g. from a Client Credentials flow or a pre-existing session):

```
use Bluerock\SuperPdp\SuperPdpConnector;

$api = new SuperPdpConnector(accessToken: 'your-access-token');

// or with the factory helper:
$api = SuperPdpConnector::withToken('your-access-token');
```

### Mode 2 — Authorization Code flow with token refresh (OAuth 2.1)

[](#mode-2--authorization-code-flow-with-token-refresh-oauth-21)

Super PDP uses rotating refresh tokens (OAuth 2.1). On each refresh the old refresh token is invalidated and replaced with a new one. **Your application is responsible for persisting the new refresh token** after every refresh call.

#### Step 1 — Redirect the user

[](#step-1--redirect-the-user)

```
use Bluerock\SuperPdp\SuperPdpConnector;

$connector = new SuperPdpConnector(
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    redirectUri: 'https://your-app.com/oauth/callback',
);

$authorizationUrl = $connector->getAuthorizationUrl();
$state = $connector->getState();

// Store $state in the session, then redirect the user to $authorizationUrl.
```

#### Step 2 — Handle the callback

[](#step-2--handle-the-callback)

```
// Validate the state from the session, then:
$authenticator = $connector->getAccessToken(
    code: $request->get('code'),
    state: $request->get('state'),
    expectedState: $session->get('oauth_state'),
);

// $authenticator is a Saloon AccessTokenAuthenticator.
// Persist the refresh token — this is your responsibility:
$myStorage->save([
    'access_token'  => $authenticator->getAccessToken(),
    'refresh_token' => $authenticator->getRefreshToken(),
    'expires_at'    => $authenticator->getExpiresAt()?->getTimestamp(),
]);
```

#### Step 3 — Authenticate subsequent requests

[](#step-3--authenticate-subsequent-requests)

```
use Saloon\Http\Auth\AccessTokenAuthenticator;

$authenticator = new AccessTokenAuthenticator(
    accessToken: $myStorage->get('access_token'),
    refreshToken: $myStorage->get('refresh_token'),
    expiresAt: new DateTimeImmutable('@' . $myStorage->get('expires_at')),
);

$connector = new SuperPdpConnector(
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    redirectUri: 'https://your-app.com/oauth/callback',
);

$connector->authenticate($authenticator);
```

#### Step 4 — Refresh when expired

[](#step-4--refresh-when-expired)

Refresh tokens rotate on every use — always persist the new one immediately:

```
if ($authenticator->hasExpired()) {
    $authenticator = $connector->refreshAccessToken($authenticator);

    // Persist the NEW tokens right away (old refresh token is now invalid):
    $myStorage->save([
        'access_token'  => $authenticator->getAccessToken(),
        'refresh_token' => $authenticator->getRefreshToken(),
        'expires_at'    => $authenticator->getExpiresAt()?->getTimestamp(),
    ]);

    $connector->authenticate($authenticator);
}
```

### Using the League OAuth2 Provider

[](#using-the-league-oauth2-provider)

If your application uses [`league/oauth2-client`](https://github.com/thephpleague/oauth2-client) directly (e.g. alongside another framework or custom OAuth middleware), you can use the bundled `SuperPdpProvider`:

```
use Bluerock\SuperPdp\OAuth\SuperPdpProvider;

$provider = new SuperPdpProvider([
    'clientId'     => 'your-client-id',
    'clientSecret' => 'your-client-secret',
    'redirectUri'  => 'https://your-app.com/oauth/callback',
    // optional:
    'baseUrl'      => 'https://api.superpdp.tech',
]);

// Build the authorization redirect:
$authUrl = $provider->getAuthorizationUrl();
$state = $provider->getState(); // Store in session for CSRF check

// Exchange the authorization code:
$token = $provider->getAccessToken('authorization_code', ['code' => $code]);

$accessToken = $token->getToken();
$refreshToken = $token->getRefreshToken();
$expiresAt = $token->getExpires(); // Unix timestamp

// Refresh:
$newToken = $provider->getAccessToken('refresh_token', [
    'refresh_token' => $refreshToken,
]);
```

The `SuperPdpProvider` implements `League\OAuth2\Client\Provider\AbstractProvider` and its `getResourceOwner()` returns a `SuperPdpResourceOwner` with `getId()`, `getNumber()`, `getFormalName()`, and `getEnv()` accessors.

---

Usage
-----

[](#usage)

### Resources

[](#resources)

The connector exposes resource classes that group related API endpoints:

```
$api->company()         // CompanyResource
$api->session()         // SessionResource
$api->invoice()         // InvoiceResource
$api->invoiceEvent()    // InvoiceEventResource
$api->directoryEntry()  // DirectoryEntryResource
$api->frenchDirectory() // FrenchDirectoryResource
```

Each resource method returns a `Saloon\Http\Response` instance.

---

### Companies

[](#companies)

```
// Get the company associated with the current access token
$response = $api->company()->me();
$company  = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\Company
```

---

### Sessions

[](#sessions)

```
// Check the onboarding/verification status of the current OAuth2 session
$response = $api->session()->me();
$session  = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\OauthSession

echo $session->company_verification_status; // 'verified' | 'needs_review' | 'failed'
```

---

### Invoices

[](#invoices)

```
// List invoices (cursor-based pagination)
$response = $api->invoice()->index(
    direction: 'out',       // 'in' | 'out' | null
    date: '2025-06',        // YYYY, YYYY-MM, or YYYY-MM-DD
    order: 'desc',
    startingAfterId: 100,
    limit: 50,
    expand: ['en_invoice'],
);
$invoices = $response->dtoOrFail(); // EntityCollection of InvoiceOverview

// Get a single invoice
$response = $api->invoice()->show(id: 42);
$invoice  = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\Invoice

// Send an invoice (XML)
$xmlContent = file_get_contents('invoice.xml');
$response   = $api->invoice()->send(
    content: $xmlContent,
    contentType: 'application/xml',
    externalId: 'my-internal-ref',
);
$invoice = $response->dtoOrFail();

// Send an invoice (PDF/Factur-X)
$pdfContent = file_get_contents('invoice.pdf');
$response   = $api->invoice()->send($pdfContent, contentType: 'application/pdf');

// Download raw invoice file
$response = $api->invoice()->download(id: 42);
$rawContent = $response->body(); // XML or PDF bytes

// Generate a test invoice (sandbox only)
$response = $api->invoice()->generateTest(format: 'ubl');
$xmlContent = $response->body();

// Convert between invoice formats
$response = $api->invoice()->convert(
    content: $xmlContent,
    from: 'ubl',
    to: 'cii',
);

// Validate one or more invoices
$response = $api->invoice()->validate([
    'invoice.xml' => file_get_contents('invoice.xml'),
]);
$report = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\ValidationReport
```

#### Cursor pagination

[](#cursor-pagination)

Super PDP uses cursor-based pagination. The list response contains `has_after` / `has_before` flags:

```
$startingAfterId = null;

do {
    $response = $api->invoice()->index(
        startingAfterId: $startingAfterId,
        limit: 100,
    );

    $body     = $response->json();
    $invoices = $response->dtoOrFail(); // EntityCollection

    foreach ($invoices as $invoice) {
        // process ...
        $startingAfterId = $invoice->id;
    }
} while ($body['has_after']);
```

---

### Invoice Events

[](#invoice-events)

```
// List all events (optionally filtered by invoice)
$response = $api->invoiceEvent()->index(
    invoiceId: 42,
    startingAfterId: null,
    limit: 100,
);
$events = $response->dtoOrFail(); // EntityCollection of InvoiceEvent

// Create an invoice event (lifecycle status update)
$response = $api->invoiceEvent()->store(
    invoiceId: 42,
    statusCode: 'fr:205', // Accepted
);
$event = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\InvoiceEvent

// Create a payment received event with details
$response = $api->invoiceEvent()->store(
    invoiceId: 42,
    statusCode: 'fr:212',
    details: [
        [
            'vat_rate'      => '20.0',
            'net_amount'    => '1000.00',
            'currency_code' => 'EUR',
            'type_code'     => 'MEN',
        ],
    ],
);
```

---

### Directory Entries

[](#directory-entries)

```
// List your company's directory entries
$response = $api->directoryEntry()->index();
$entries  = $response->dtoOrFail(); // EntityCollection of DirectoryEntry

// Get a specific entry
$response = $api->directoryEntry()->show(id: 5);
$entry    = $response->dtoOrFail(); // Bluerock\SuperPdp\Entities\DirectoryEntry

// Create an entry (register an address on Peppol or PPF)
$response = $api->directoryEntry()->store(
    directory: 'peppol',       // 'peppol' | 'ppf'
    identifier: '0225:853322915',
);

// Delete an entry
$api->directoryEntry()->delete(id: 5);
```

---

### French Directory

[](#french-directory)

These endpoints are **public** (no authentication required) and allow you to look up companies registered in the French e-invoicing directory.

```
// Search companies by name
$response  = $api->frenchDirectory()->companies(
    formalNameStartsWith: 'Acme',
    postCodeStartsWith: '75',
    limit: 50,
);
$companies = $response->dtoOrFail(); // EntityCollection of FrenchDirectoryCompany

// Search by SIREN number
$response  = $api->frenchDirectory()->companies(number: '853322915');

// List electronic addresses for a company (by SIREN)
$response = $api->frenchDirectory()->entries(number: '853322915');
$entries  = $response->dtoOrFail(); // EntityCollection of FrenchDirectoryEntry

foreach ($entries as $entry) {
    if ($entry->is_active) {
        echo $entry->identifier; // e.g. '0225:853322915'
    }
}
```

---

Entities
--------

[](#entities)

Entities are typed Data Transfer Objects (DTOs). Each request returns a `Saloon\Http\Response`; call `dtoOrFail()` to get the entity.

EntityDescription`Company`Your company profile`OauthSession`Current OAuth2 session and verification status`Invoice`Full invoice with events and `en_invoice` payload`InvoiceOverview`Lightweight invoice listing object`InvoiceEvent`A lifecycle event on an invoice`DirectoryEntry`A Peppol or PPF address entry`FrenchDirectoryCompany`A company from the French e-invoicing directory`FrenchDirectoryEntry`An active address from the French directory`ValidationReport`Result of invoice validationAll entities can be created from arrays and exported:

```
$company = Company::fromArray($data);
$arr     = $company->toArray();               // all properties
$arr     = $company->toArray(filter: true);   // non-null only
```

The `en_invoice` field on `Invoice` and `InvoiceOverview` is exposed as a raw `array` matching the EN 16931 structure described in the API spec.

---

Responses
---------

[](#responses)

All resource methods return a `Saloon\Http\Response`:

```
$response->ok();         // true for 2xx
$response->failed();     // true for 4xx/5xx
$response->status();     // HTTP status code
$response->json();       // response body as array
$response->body();       // raw string body
$response->dto();        // entity or null
$response->dtoOrFail();  // entity or throws on non-2xx
```

---

Testing
-------

[](#testing)

```
composer install
./vendor/bin/pest
```

Tests use Saloon's built-in `MockClient` to mock HTTP responses without making real network calls:

```
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;

$mockClient = new MockClient([
    MockResponse::make(['id' => 1, ...], 200),
]);

$connector = new SuperPdpConnector('test-token');
$connector->withMockClient($mockClient);

$response = $connector->company()->me();
```

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance63

Regular maintenance activity

Popularity0

Limited adoption so far

Community7

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/d7c605c71ebef94596a6c0f89e40a62e0d685b052288fe8a0017a0378c85823f?d=identicon)[tgeorgel](/maintainers/tgeorgel)

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

---

Top Contributors

[![tgeorgel](https://avatars.githubusercontent.com/u/11785727?v=4)](https://github.com/tgeorgel "tgeorgel (1 commits)")

### Embed Badge

![Health badge](/badges/bluerock-superpdp-php-client/health.svg)

```
[![Health](https://phpackages.com/badges/bluerock-superpdp-php-client/health.svg)](https://phpackages.com/packages/bluerock-superpdp-php-client)
```

###  Alternatives

[facebook/php-business-sdk

PHP SDK for Facebook Business

90923.5M35](/packages/facebook-php-business-sdk)[exsyst/swagger

A php library to manipulate Swagger specifications

35916.3M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24015.5M18](/packages/hubspot-api-client)[botman/driver-telegram

Telegram driver for BotMan

93452.6k6](/packages/botman-driver-telegram)

PHPackages © 2026

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