factpulse / sdk
REST API for electronic invoicing in France: Factur-X (CII), UBL 2.1, AFNOR PDP/PA, electronic signatures. ## 🎯 Main Features ### 📄 Invoice Generation - **Formats**: CII XML, UBL 2.1 XML, or Factur-X PDF/A-3 - **Profiles** (CII/PDF): MINIMUM, BASIC, EN16931, EXTENDED - **UBL**: Always EN16931 comp
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- guzzlehttp/guzzle: ^7.3
- guzzlehttp/psr7: ^1.7 || ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.5
- phpunit/phpunit: ^8.0 || ^9.0
- dev-main
- v4.3.0
- v4.1.2
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.0.37
- v3.0.36
- v3.0.35
- v3.0.34
- v3.0.33
- v3.0.32
- v3.0.31
- v3.0.30
- v3.0.29
- v3.0.28
- v3.0.27
- v3.0.26
- v3.0.25
- v3.0.24
- v3.0.23
- v3.0.19
- v3.0.18
- v3.0.17
- v3.0.16
- v3.0.15
- v3.0.14
- v3.0.13
- v3.0.12
- v3.0.11
- v3.0.10
- v3.0.9
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.0.42
- v2.0.41
- v2.0.40
- v2.0.39
- v2.0.38
- v2.0.37
- v2.0.36
- v2.0.35
- v2.0.34
- v2.0.33
- v2.0.32
- v2.0.31
- v2.0.30
- v2.0.29
- v2.0.28
- v2.0.27
- v2.0.26
- v2.0.25
- v2.0.24
- v2.0.23
- v2.0.22
- v2.0.21
- v2.0.20
- v2.0.19
- v2.0.18
- v2.0.17
- v2.0.16
- v2.0.15
- v2.0.14
- v2.0.13
- v2.0.12
- v2.0.11
- v2.0.10
- v2.0.9
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.0
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.8
- v1.0.0
- v0.1.0
This package is auto-updated.
Last update: 2026-03-31 09:50:31 UTC
README
Official PHP client for the FactPulse API - French electronic invoicing.
Features
- Factur-X: Generation and validation of electronic invoices (MINIMUM, BASIC, EN16931, EXTENDED profiles)
- Chorus Pro: Integration with the French public invoicing platform
- AFNOR PDP/PA: Submission of flows compliant with XP Z12-013 standard
- Electronic signature: PDF signing (PAdES-B-B, PAdES-B-T, PAdES-B-LT)
- Thin HTTP wrapper: Generic
post()andget()methods with automatic JWT auth and polling
Installation
composer require factpulse/sdk
Quick Start
<?php require_once 'vendor/autoload.php'; use FactPulse\SDK\FactPulseClient; // Create the client $client = new FactPulseClient( "your_email@example.com", "your_password", "your-client-uuid" // From dashboard: Configuration > Clients ); // Read your source PDF $pdfB64 = base64_encode(file_get_contents("source_invoice.pdf")); // Generate Factur-X and submit to PDP in one call $result = $client->post("processing/invoices/submit-complete-async", [ "invoiceData" => [ "number" => "INV-2025-001", "supplier" => [ "name" => "ACME Corporation", "siret" => "12345678901234", "iban" => "FR7630001007941234567890185", "routing_address" => "12345678901234", ], "recipient" => [ "name" => "Client Company SA", "siret" => "98765432109876", "routing_address" => "98765432109876", ], "lines" => [ [ "description" => "Consulting services", "quantity" => 10, "unitPrice" => 100.0, "vatRate" => 20.0, ], ], ], "sourcePdf" => $pdfB64, "profile" => "EN16931", "destination" => ["type" => "afnor"], ]); // PDF is in $result["content"] (auto-polled, auto-decoded from base64) file_put_contents("facturx_invoice.pdf", $result["content"]); echo "Flow ID: " . $result["afnorResult"]["flowId"] . "\n";
API Methods
The SDK provides two generic methods that map directly to API endpoints:
// POST /api/v1/{path} $result = $client->post("path/to/endpoint", ["key1" => $value1, "key2" => $value2]); // GET /api/v1/{path} $result = $client->get("path/to/endpoint", ["param1" => $value1]);
Common Endpoints
| Endpoint | Method | Description |
|---|---|---|
processing/invoices/submit-complete-async |
POST | Generate Factur-X + submit to PDP |
processing/generate-invoice |
POST | Generate Factur-X XML or PDF |
processing/validate-xml |
POST | Validate Factur-X XML |
processing/validate-facturx-pdf |
POST | Validate Factur-X PDF |
processing/sign-pdf |
POST | Sign PDF with certificate |
afnor/flow/v1/flows |
POST | Submit flow to AFNOR PDP |
afnor/incoming-flows/{flow_id} |
GET | Get incoming invoice |
chorus-pro/factures/soumettre |
POST | Submit to Chorus Pro |
Webhooks
Instead of polling, you can receive results via webhook by adding callbackUrl:
// Submit with webhook - returns immediately $result = $client->post("processing/invoices/submit-complete-async", [ "invoiceData" => $invoiceData, "sourcePdf" => $pdfB64, "destination" => ["type" => "afnor"], "callbackUrl" => "https://your-server.com/webhook/factpulse", "webhookMode" => "INLINE", // or "DOWNLOAD_URL" ]); $taskId = $result["taskId"]; // Result will be POSTed to your webhook URL
Webhook Receiver Example
<?php $webhookSecret = "your-shared-secret"; function verifySignature(string $payload, string $signature): bool { if (strpos($signature, "sha256=") !== 0) { return false; } $expected = hash_hmac("sha256", $payload, $GLOBALS["webhookSecret"]); return hash_equals($expected, substr($signature, 7)); } // Get raw POST body $payload = file_get_contents("php://input"); $signature = $_SERVER["HTTP_X_WEBHOOK_SIGNATURE"] ?? ""; if (!verifySignature($payload, $signature)) { http_response_code(401); echo json_encode(["error" => "Invalid signature"]); exit; } $event = json_decode($payload, true); $eventType = $event["event_type"]; $data = $event["data"]; if ($eventType === "submission.completed") { $flowId = $data["afnorResult"]["flowId"] ?? null; error_log("Invoice submitted: $flowId"); } elseif ($eventType === "submission.failed") { error_log("Submission failed: " . ($data["error"] ?? "Unknown")); } header("Content-Type: application/json"); echo json_encode(["status" => "received"]);
Webhook Event Types
| Event | Description |
|---|---|
generation.completed |
Factur-X generated successfully |
generation.failed |
Generation failed |
validation.completed |
Validation passed |
validation.failed |
Validation failed |
signature.completed |
PDF signed |
submission.completed |
Submitted to PDP/Chorus |
submission.failed |
Submission failed |
Zero-Storage Mode
Pass PDP credentials directly in the request (no server-side storage):
$result = $client->post("processing/invoices/submit-complete-async", [ "invoiceData" => $invoiceData, "sourcePdf" => $pdfB64, "destination" => [ "type" => "afnor", "flowServiceUrl" => "https://api.pdp.example.com/flow/v1", "tokenUrl" => "https://auth.pdp.example.com/oauth/token", "clientId" => "your_pdp_client_id", "clientSecret" => "your_pdp_client_secret", ], ]);
Error Handling
use FactPulse\SDK\FactPulseClient; use FactPulse\SDK\FactPulseError; try { $result = $client->post("processing/validate-xml", ["xmlContent" => $xml]); } catch (FactPulseError $e) { echo "Error: " . $e->getMessage() . "\n"; echo "Status code: " . $e->getStatusCode() . "\n"; echo "Details: " . print_r($e->getDetails(), true) . "\n"; }
Available Helpers
The SDK provides the following helper classes:
FactPulseClient: Main HTTP client with auto-auth and pollingFactPulseError: Base exception classFactPulseAuthError: Authentication failureFactPulseValidationError: Validation errors with detailsFactPulsePollingTimeout: Task polling timeout
Resources
- API Documentation: https://factpulse.fr/api/facturation/documentation
- Support: contact@factpulse.fr
License
MIT License - Copyright (c) 2025 FactPulse