ophelix / ophelix-php
Bibliothèque PHP officielle de l'API Ophelix : facturation de l'Assurance Maladie.
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
Requires (Dev)
- laravel/pint: ^1.18
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
La bibliothèque PHP officielle de l'API Ophelix : la facturation de l'Assurance Maladie (FSE, SESAM-Vitale) par une API.
- PHP 8.2 et plus, sans framework.
- PSR-18 et PSR-17 : Guzzle par défaut, tout autre client sur demande.
- Un service par ressource, des objets typés pour PHPStan et Psalm, une exception par type d'erreur.
- Clé d'idempotence tirée pour chaque écriture, reprises sur 429 et 5xx, pagination automatique.
- Vérification de la signature des webhooks.
Installation
composer require ophelix/ophelix-php
Premier appel, en mode test
Créez une clé secrète dans Développeurs › Clés sur app.ophelix.fr. Une clé sk_test_ travaille sur le simulateur : elle ne touche ni l'Assurance Maladie ni une vraie carte.
use Ophelix\OphelixClient; $ophelix = new OphelixClient(getenv('OPHELIX_API_KEY')); // Un praticien naît de la lecture de sa carte CPS, un patient de celle de sa carte Vitale. $practitioner = $ophelix->practitioners->create(['card' => 'cps_doctor']); $coverage = $ophelix->coverages->create(['source' => 'vitale_card', 'card' => 'vitale_ok']); $claim = $ophelix->claims->create([ 'practitioner' => $practitioner->id, 'patient' => $coverage->patient, 'third_party_amo' => true, 'context' => ['care_pathway' => 'treating_doctor', 'common_law_accident' => false], 'lines' => [['nomenclature' => 'ngap', 'code' => 'ZC', 'service_date' => '2026-11-06', 'unit_price_cents' => 2650]], ]); $ophelix->claims->finalize($claim->id); $ophelix->claims->secure($claim->id); // En mode test, les retours de l'Assurance Maladie se déclenchent à la main. $ophelix->testHelpers->claims->simulateReturn($claim->id, ['scenario' => 'arl_positive']); $paid = $ophelix->testHelpers->claims->simulateReturn($claim->id, ['scenario' => 'payment']); echo $paid->status; // paid
Le parcours complet est dans le guide de démarrage.
Ce que fait le client
| Réglage | Par défaut | Sens |
|---|---|---|
api_version |
celle que la bibliothèque épingle | L'en-tête Ophelix-Version |
base_url |
https://api.ophelix.fr |
L'adresse de l'API, sans /v1 |
max_retries |
2 |
Les reprises après un 429, un 5xx ou une coupure |
timeout |
60 |
Le délai d'une tentative, en secondes, avec le client Guzzle par défaut |
http_client, request_factory, stream_factory |
Guzzle | Un autre client PSR-18 et ses fabriques PSR-17 |
$ophelix = new OphelixClient($key, ['max_retries' => 4, 'timeout' => 20.0]); $ophelix->livemode; // false pour une clé de test
Chaque méthode accepte en dernier argument des options d'appel : idempotency_key, api_version, max_retries.
Objets
Un objet se lit par ses propriétés, ou comme un tableau pour un champ plus récent que la bibliothèque. Un champ lié rend un identifiant, ou l'objet avec expand :
$claim = $ophelix->claims->retrieve('clm_…', ['expand' => ['patient']]); $claim->patient->quality; // un Ophelix\Resources\Patient $claim->getLastResponse()->requestId; // à retrouver dans Développeurs › Requêtes
Un objet passé en paramètre part par son identifiant : 'patient' => $claim->patient marche, déplié ou non.
Listes
all() rend la première page. Un foreach les parcourt toutes, en demandant la suivante quand la précédente est lue :
$page = $ophelix->claims->all(['status' => 'paid', 'limit' => 50]); $page->data; // cette page $page->has_more; foreach ($ophelix->claims->all(['status' => 'paid']) as $claim) { // toutes les factures payées }
Idempotence et reprises
Chaque écriture porte une clé Idempotency-Key tirée pour elle, et la garde d'une reprise à l'autre : un 429, un 5xx ou une coupure se rejouent sans doubler l'écriture. Passez la vôtre pour rejouer un appel d'un processus à l'autre :
$ophelix->claims->create($params, ['idempotency_key' => $commande->uuid]);
Erreurs
Une erreur de l'API lève la sous-classe de son type, qui étend Ophelix\Exception\ApiErrorException :
| Exception | Type | Exemple |
|---|---|---|
InvalidRequestException |
invalid_request_error |
paramètre faux, objet inconnu |
AuthenticationException |
authentication_error |
clé inconnue ou révoquée |
PermissionException |
permission_error |
droit manquant sur une clé restreinte |
RateLimitException |
rate_limit_error |
débit dépassé, après les reprises |
IdempotencyException |
idempotency_error |
même clé, autre corps |
ApiException |
api_error |
erreur d'Ophelix, moteur indisponible |
ApiConnectionException dit que l'API n'a pas répondu. Toutes implémentent Ophelix\Exception\ExceptionInterface.
use Ophelix\Exception\InvalidRequestException; try { $ophelix->claims->retrieve('clm_inconnu'); } catch (InvalidRequestException $exception) { $exception->getErrorCode(); // resource_missing $exception->getDocUrl(); // https://docs.ophelix.fr/erreurs#resource_missing $exception->getRequestId(); // req_… }
Webhooks
Vérifiez chaque livraison sur le corps brut, avant tout décodage :
use Ophelix\Exception\SignatureVerificationException; use Ophelix\Webhook; try { $event = Webhook::constructEvent( file_get_contents('php://input'), $_SERVER['HTTP_OPHELIX_SIGNATURE'] ?? null, getenv('OPHELIX_WEBHOOK_SECRET'), ); } catch (SignatureVerificationException) { http_response_code(400); exit; } if ($event->type === 'claim.paid') { $claim = $event->data['object']; // un Ophelix\Resources\Claim }
Une livraison signée il y a plus de cinq minutes est refusée. Webhook::generateTestHeader() signe un corps pour tester votre point de terminaison.
Versions
La bibliothèque suit le versionnage sémantique. Chaque version épingle une version datée de l'API (OphelixClient::API_VERSION) : une réponse n'y perd ni ne renomme jamais un champ. Voir CHANGELOG.md.
Licence
MIT. Voir LICENSE.