French e-invoicing (2026 reform)¶
France mandates electronic invoicing for domestic B2B transactions:
- September 2026: every company must be able to receive e-invoices; large companies and mid-caps must also emit them.
- September 2027: emission becomes mandatory for SMEs and micro-companies.
B2B invoices must transit through a Plateforme Agréée (PA), a government-accredited platform, in a structured format: Factur-X (PDF with embedded XML), UBL or CII. Pennylane is itself an accredited PA, so Pennylane users need no third-party platform. The API exposes the whole flow, and this SDK covers all of it.
Check your PA registrations¶
<?php
use Pennylane\Sdk\Pennylane;
$client = new Pennylane(); // reads PENNYLANE_API_TOKEN
foreach ($client->paRegistrations->list() as $registration) {
echo $registration->siret . ' ' . $registration->status . PHP_EOL;
}
Send an invoice through the PA network¶
Finalize the invoice, then hand it to the platform:
$invoice = $client->customerInvoices->finalize($draft->id);
$client->customerInvoices->sendToPa($invoice->id);
Track the delivery status either on the invoice's e-invoicing field ($invoice->eInvoicing->status), via the change feed, or with the webhook event customer_invoice.e_invoicing_status_updated (see the webhooks guide).
Receive supplier e-invoices¶
Invoices arriving through the network land in Pennylane automatically. If you receive structured invoices through another channel, import them:
use Pennylane\Sdk\FileUpload;
$client->supplierInvoices->importEInvoice(FileUpload::fromPath('invoice.xml'));
The Factur-X or XML data is parsed structurally (no OCR), so supplier, amounts and lines are reliable.
Import sales e-invoices from another tool¶
If you invoice from an external ERP but keep Pennylane as your accounting system and PA, push your sales invoices as e-invoices:
$client->customerInvoices->importEInvoice(
FileUpload::fromPath('factur-x.pdf'),
invoiceOptions: [
// pre-fills the customer and matches invoice lines by e_invoice_line_id
// (the Factur-X BT-126 LineID), see the method docblock
'customer_id' => 123,
],
);
Update e-invoice status (advanced)¶
Purchase-side workflows can update the lifecycle status of a received supplier e-invoice:
$client->supplierInvoices->updateEInvoiceStatus(
$invoiceId,
status: 'approved',
);
// Disputing a line requires a reason code
$client->supplierInvoices->updateEInvoiceStatus(
$invoiceId,
status: 'disputed',
reason: 'incorrect_vat_rate',
);
status is one of disputed, refused or approved. reason is required with disputed (for example incorrect_vat_rate, incorrect_unit_prices, incorrect_billed_quantity, defective_delivered_item, bank_details_error) or with refused (for example incorrect_vat_rate, contract_completed, duplicate_invoice, non_compliant_invoice); it is omitted when status is approved.
Deadlines are business-critical
If you integrate invoicing for September 2026, test the PA flow early in a sandbox. The SDK raises ValidationException with the API's details when a document does not meet the format requirements.