PHP code example of scell / sdk

1. Go to this page and download the library: Download scell/sdk library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

scell / sdk example snippets


use Scell\Sdk\ScellApiClient;
use Scell\Sdk\DTOs\Address;
use Scell\Sdk\Enums\AuthMethod;

// Initialisation
$api = ScellApiClient::withApiKey('tk_live_...');

// Mode sandbox pour les tests
$api = ScellApiClient::sandbox('tk_test_...');

use Scell\Sdk\DTOs\Address;

$invoice = $api->invoices()->builder()
    ->externalId('my-internal-id')
    ->outgoing()
    ->facturX()
    ->issueDate(new DateTime())
    ->dueDate((new DateTime())->modify('+30 days'))
    ->seller(
        siret: '12345678901234',
        name: 'Ma Societe SARL',
        address: new Address(
            line1: '1 Rue de la Paix',
            postalCode: '75001',
            city: 'Paris'
        )
    )
    ->buyer(
        siret: '98765432109876',
        name: 'Client SA',
        address: new Address(
            line1: '2 Avenue du Commerce',
            postalCode: '69001',
            city: 'Lyon'
        )
    )
    ->addLine('Prestation de conseil', 10, 100.00, 20.0)
    ->addLine('Formation', 2, 500.00, 20.0)
    ->archiveEnabled()
    ->create();

echo "Facture creee: {$invoice->id}";
echo "Total TTC: {$invoice->totalTtc} EUR";

> **Note:** Invoice and credit note numbers are automatically generated by Scell.io. Draft documents receive a temporary number (DRAFT-XXXXX-00001), and the definitive fiscal number (XXXXX-YYYYMM-00001) is assigned at submission.

$invoice = $client->invoices()->create([
    'issue_date' => '2026-03-29',
    'due_date' => '2026-04-28',
    'currency' => 'EUR',
    // Vendeur francais (SIRET requis)
    'seller_siret' => '12345678901234',
    'seller_name' => 'Ma Société SAS',
    'seller_country' => 'FR',
    'seller_vat_number' => 'FR12345678901',
    'seller_address' => ['line1' => '10 rue de Paris', 'postal_code' => '75001', 'city' => 'Paris', 'country' => 'FR'],
    // Acheteur belge (pas de SIRET, numero de TVA)
    'buyer_name' => 'Entreprise Belge SPRL',
    'buyer_country' => 'BE',
    'buyer_vat_number' => 'BE0123456789',
    'buyer_address' => ['line1' => '15 Avenue Louise', 'postal_code' => '1050', 'city' => 'Bruxelles', 'country' => 'BE'],
    'lines' => [
        ['description' => 'Consulting services', 'quantity' => 10, 'unit_price' => 150.00, 'vat_rate' => 0],
    ],
    'format' => 'ubl',
]);

$invoice = $client->invoices()->create([
    'issue_date' => '2026-03-29',
    'due_date' => '2026-04-28',
    'currency' => 'GBP',
    'seller_siret' => '12345678901234',
    'seller_name' => 'Ma Société SAS',
    'seller_country' => 'FR',
    'seller_vat_number' => 'FR12345678901',
    'seller_address' => ['line1' => '10 rue de Paris', 'postal_code' => '75001', 'city' => 'Paris', 'country' => 'FR'],
    // Acheteur UK — legal_id avec schema
    'buyer_name' => 'British Ltd',
    'buyer_country' => 'GB',
    'buyer_vat_number' => 'GB123456789',
    'buyer_legal_id' => '12345678',
    'buyer_legal_id_scheme' => '0088',
    'buyer_address' => ['line1' => '20 Baker Street', 'postal_code' => 'W1U 3BW', 'city' => 'London', 'country' => 'GB'],
    'lines' => [
        ['description' => 'Design services', 'quantity' => 5, 'unit_price' => 200.00, 'vat_rate' => 0],
    ],
    'format' => 'ubl',
]);

use Scell\Sdk\Enums\AuthMethod;

$signature = $api->signatures()->builder()
    ->title('Contrat de prestation')
    ->description('Contrat annuel de maintenance')
    ->externalId('contract-2024-001')
    ->documentFromFile('/path/to/contract.pdf')
    ->addEmailSigner('Jean', 'Dupont', '[email protected]')
    ->addSmsSigner('Marie', 'Martin', '+33612345678')
    ->addSignaturePosition(page: 5, x: 100, y: 700, width: 200, height: 50)
    ->uiConfig([
        'sidebar_logo' => 'https://example.com/logo.png',
        'sidebar_background_color' => '#0066CC',
    ])
    ->redirectUrls(
        completeUrl: 'https://example.com/signed',
        cancelUrl: 'https://example.com/cancelled'
    )
    ->expiresAt((new DateTime())->modify('+7 days'))
    ->create();

echo "Signature creee: {$signature->id}";
echo "Statut: {$signature->status->label()}";

// Recuperer les URLs de signature
foreach ($signature->signers as $signer) {
    echo "{$signer->fullName()}: {$signer->signingUrl}";
}

$signature = $api->signatures()->builder()
    ->title('Contrat NDA')
    ->documentFromFile('/path/to/nda.pdf')
    ->addEmailSigner('Jean', 'Dupont', '[email protected]', message: 'Bonjour, votre code OTP : {OTP}')
    ->addSignaturePosition(page: 1, x: 70, y: 85, width: 20, height: 5, unit: 'percent')
    ->uiConfig([
        'sidebar_logo'             => 'https://cdn.example.com/logo.svg',
        'sidebar_background_color' => '#0F172A',
        'sidebar_text_color'       => '#FFFFFF',
        'hide_branding'            => true,
        'iframe_ancestors'         => ['https://app.example.com'],
    ])
    ->signatureOptions([
        'signature_mode'     => 'both',           // 'draw' | 'type' | 'both'
        'signer_must_read'   => true,
        'user_editable_data' => false,
        'timezone'           => 'Europe/Paris',
    ])
    ->create();

$signature = $api->signatures()->builder()
    ->title('Contrat bipartite multi-pages')
    ->documentFromFile('/path/to/contract.pdf')
    ->addEmailSigner('Jean', 'Dupont', '[email protected]')   // index 0
    ->addSmsSigner('Marie', 'Martin', '+33612345678')        // index 1

    // Le signataire 0 signe sur DEUX pages (1 et 3).
    ->addSignaturePosition(page: 1, x: 70, y: 80, signerIndex: 0)
    ->addSignaturePosition(page: 3, x: 70, y: 80, signerIndex: 0)

    // Le signataire 1 signe une seule fois (page 3).
    ->addSignaturePosition(page: 3, x: 30, y: 80, signerIndex: 1)

    ->create();

use Scell\Sdk\DTOs\BlockPosition;
use Scell\Sdk\DTOs\DateBlock;
use Scell\Sdk\DTOs\InitialsBlock;
use Scell\Sdk\DTOs\Mention;

$signature = $api->signatures()->builder()
    ->title('Contrat de prestation')
    ->documentFromFile('/path/to/contract.pdf')
    ->addEmailSigner('Jean', 'Dupont', '[email protected]')

    // 1) Bloc paraphe : initiales auto sur toutes les pages (sauf derniere).
    //    Accepte un tableau brut OU un DTO InitialsBlock.
    ->initialsBlock([
        'enabled'   => true,
        'mode'      => 'auto',           // 'auto' | 'custom'
        'source'    => 'signer_name',    // 'signer_name' | 'custom'
        'pages'     => 'except_last',    // 'all' | 'except_last' | [1,2,5]
        'position'  => ['x' => 90, 'y' => 95, 'unit' => 'percent'],
        'font_size' => 10,
        'color'     => '#333333',
    ])

    // 2) Mentions juridiques per-signer (label + position + signer_index 0-based).
    //    Le signataire saisit le texte (

use Scell\Sdk\ScellClient;

$client = new ScellClient($bearerToken);

// Lister les factures
$invoices = $client->invoices()->list([
    'direction' => 'outgoing',
    'status' => 'validated',
    'per_page' => 50,
]);

foreach ($invoices->data as $invoice) {
    echo "{$invoice->invoiceNumber}: {$invoice->totalTtc} EUR";
}

// Pagination
if ($invoices->hasNextPage()) {
    $nextPage = $client->invoices()->list(['page' => $invoices->nextPage()]);
}

// Consulter le solde
$balance = $client->balance()->get();
echo "Solde: {$balance->formatted()}";

if ($balance->isLow()) {
    echo "Attention: solde bas!";
}

// Gerer les entreprises
$companies = $client->companies()->list();

// Gerer les webhooks
$webhooks = $client->webhooks()->list();

use Scell\Sdk\ScellApiClient;

$api = ScellApiClient::withApiKey('tk_live_...');

// Étape 1 : Créer une session d'onboarding
$session = $api->onboarding()->createSession([
    'partner_ref'  => 'mon-id-utilisateur-interne',
    'redirect_url' => 'https://monapp.com/onboarding/complete',
]);

// Étape 2 : Obtenir l'URL d'autorisation SuperPDP
$auth = $api->onboarding()->getSuperPDPAuthorizeUrl($session->id);
// Ouvrir $auth['authorize_url'] dans un popup côté frontend
// SuperPDP gère l'inscription, le KYB et la vérification d'identité

// Étape 3 : Réceptionner le callback SuperPDP (code + state)
$result = $api->onboarding()->superpdpCallback(
    sessionId: $session->id,
    code: $request->input('code'),
    state: $request->input('state'),
);

if ($result['success']) {
    $tenant = $result['tenant'];
    echo "Tenant enrôlé : {$tenant['name']} — SIRET {$tenant['siret']}";
    // $tenant : ['id', 'name', 'siret', 'environment']
}

// Consulter le statut d'une session
$status = $api->onboarding()->getSession($session['id']);

use Scell\Sdk\Enums\RejectionCode;
use Scell\Sdk\Enums\DisputeType;

// Lister les factures entrantes
$incoming = $api->invoices()->incoming([
    'status' => 'pending',
    'per_page' => 25,
]);

foreach ($incoming->data as $invoice) {
    echo "{$invoice->invoiceNumber} - {$invoice->sellerName}: {$invoice->totalTtc} EUR";
}

// Accepter une facture
$invoice = $api->invoices()->accept($invoiceId, [
    'comment' => 'Facture conforme',
]);
echo "Facture acceptee: {$invoice->status->label()}";

// Rejeter une facture avec un code de rejet
$invoice = $api->invoices()->reject(
    $invoiceId,
    'Le montant ne correspond pas a la commande',
    RejectionCode::IncorrectAmount
);

// Contester une facture (litige)
$invoice = $api->invoices()->dispute(
    $invoiceId,
    'Montant facture: 1500 EUR, montant commande: 1200 EUR',
    DisputeType::AmountDispute,
    expectedAmount: 1200.00
);

// Marquer une facture comme payee (obligatoire dans le cycle de vie)
$invoice = $api->invoices()->markPaid($invoiceId, [
    'payment_reference' => 'VIR-2026-0124',
    'paid_at' => '2026-01-24T10:30:00Z',
    'note' => 'Paiement recu par virement bancaire'
]);
echo "Facture payee: {$invoice->status->label()}";
echo "Reference: {$invoice->paymentReference}";

// Verifier si une facture est payee
if ($invoice->isPaid()) {
    echo "Facture reglee le: {$invoice->paidAt->format('d/m/Y')}";
}

// Obtenir une URL temporaire de telechargement (15 min)
$download = $api->invoices()->download($invoiceId, 'converted');
echo "URL: {$download['url']}";

// Telecharger le contenu binaire d'une facture PDF (Factur-X)
$pdfContent = $api->invoices()->downloadContent($invoiceId);
file_put_contents('facture.pdf', $pdfContent);

// Telecharger le contenu XML (UBL/CII)
$xmlContent = $api->invoices()->downloadContent($invoiceId, 'xml');
file_put_contents('facture.xml', $xmlContent);

// Telecharger un document signe
$download = $api->signatures()->download($signatureId, 'signed');

// Convertir une facture vers un autre format
$api->invoices()->convert($invoiceId, OutputFormat::UBL);

// Piste d'audit (factures)
$audit = $api->invoices()->auditTrail($invoiceId);
foreach ($audit['data'] as $entry) {
    echo "{$entry['action']}: {$entry['details']}";
}

// Piste d'audit (signatures)
$audit = $api->signatures()->auditTrail($signatureId);
foreach ($audit['data'] as $entry) {
    echo "{$entry['action']}: {$entry['details']}";
}

// Lister les sub-tenants
$subTenants = $api->subTenants()->list(['per_page' => 50]);

foreach ($subTenants->data as $subTenant) {
    echo "{$subTenant->name} ({$subTenant->siret})";
}

// Creer un sub-tenant
$subTenant = $api->subTenants()->create([
    'external_id' => 'CLIENT-001',
    'name' => 'Mon Client SARL',
    'siret' => '12345678901234',
    'email' => '[email protected]',
    'address_line1' => '1 rue de la Paix',
    'postal_code' => '75001',
    'city' => 'Paris',
]);

// Rechercher par ID externe
$subTenant = $api->subTenants()->findByExternalId('CLIENT-001');

// Mettre a jour
$subTenant = $api->subTenants()->update($subTenantId, [
    'email' => '[email protected]',
]);

// Creer une facture pour un sub-tenant
$invoice = $api->tenantInvoices()->createForSubTenant($subTenantId, [
    'direction' => 'outgoing',
    'output_format' => 'facturx',
    'issue_date' => '2026-01-26',
    'seller' => [...],
    'buyer' => [...],
    'lines' => [
        [
            'description' => 'Prestation de service',
            'quantity' => 1,
            'unit_price' => 100.00,
            'tax_rate' => 20.00,
            'total_ht' => 100.00,
            'total_ttc' => 120.00,
        ],
    ],
    // Totaux niveau facture — OBLIGATOIRES.
    // Attention : la cle TVA est `total_tax` (et NON `total_tva`).
    'total_ht' => 100.00,
    'total_tax' => 20.00,
    'total_ttc' => 120.00,
]);

// Soumettre pour traitement
$api->tenantInvoices()->submit($invoiceId);

// Factures directes (sans sub-tenant) — memes totaux obligatoires
$invoice = $api->directInvoices()->create([
    'direction' => 'outgoing',
    'output_format' => 'facturx',
    'issue_date' => '2026-01-26',
    'seller' => [...],
    'buyer' => [...],
    'lines' => [...],
    'total_ht' => 100.00,
    'total_tax' => 20.00,
    'total_ttc' => 120.00,
]);

// Operations en masse
$api->directInvoices()->bulkCreate([...]);
$api->directInvoices()->bulkSubmit([$id1, $id2, $id3]);

// Factures entrantes (fournisseurs)
$incoming = $api->incomingInvoices()->listForSubTenant($subTenantId, [
    'status' => 'received',
]);

// Accepter / Rejeter / Marquer comme payee
$api->incomingInvoices()->accept($invoiceId);
$api->incomingInvoices()->reject($invoiceId, 'Montant incorrect');
$api->incomingInvoices()->markPaid($invoiceId, 'VIR-2026-001');

// Dashboard de conformite
$compliance = $api->fiscal()->compliance();

// Verification d'integrite
$report = $api->fiscal()->integrity();
$history = $api->fiscal()->integrityHistory(['per_page' => 25]);

// Clotures
$closings = $api->fiscal()->closings();
$api->fiscal()->performDailyClosing();

// Export FEC
$fec = $api->fiscal()->fecExport(['year' => 2025]);

// Attestation annuelle
$attestation = $api->fiscal()->attestation(2025);
$pdf = $api->fiscal()->attestationDownload(2025);

// Ecritures comptables
$entries = $api->fiscal()->entries(['per_page' => 100]);

// Ancres d'integrite
$anchors = $api->fiscal()->anchors();

// Regles fiscales
$rules = $api->fiscal()->rules();
$api->fiscal()->createRule([...]);

// Registre des mesures
$pdf = $api->fiscal()->downloadMeasuresRegister();
file_put_contents('registre-mesures-isca.pdf', $pdf);

// Dossier technique
$pdf = $api->fiscal()->downloadTechnicalDossier();
file_put_contents('dossier-technique-isca.pdf', $pdf);

// Auto-attestation ISCA
$pdf = $api->fiscal()->downloadSelfAttestation();
file_put_contents('auto-attestation-isca.pdf', $pdf);

// Vue d'ensemble
$stats = $api->stats()->overview();

// Statistiques mensuelles
$monthly = $api->stats()->monthly(['year' => 2025]);

// Stats par sub-tenant
$stats = $api->stats()->subTenantOverview($subTenantId);

// Facturation plateforme
$invoices = $api->billing()->invoices();
$usage = $api->billing()->usage();
$transactions = $api->billing()->transactions();

// Recharger le solde
$api->billing()->topUp(['amount' => 100.00]);

// Confirmer un rechargement (apres validation paiement)
$api->billing()->confirmTopUp(['payment_intent_id' => 'pi_...']);

// Lister les avoirs
$creditNotes = $api->creditNotes()->list($subTenantId, [
    'status' => 'draft',
    'per_page' => 25,
]);

// Verifier les montants creditables
$remaining = $api->creditNotes()->remainingCreditable($invoiceId);

// Creer un avoir partiel
$creditNote = $api->creditNotes()->create($subTenantId, [
    'invoice_id' => $invoiceId,
    'reason' => 'Remise commerciale',
    'type' => 'partial',
    'items' => [
        [
            'description' => 'Remise sur prestation',
            'quantity' => 1,
            'unit_price' => 100.00,
            'tax_rate' => 20.0,
        ],
    ],
]);

// Creer un avoir total
$creditNote = $api->creditNotes()->create($subTenantId, [
    'invoice_id' => $invoiceId,
    'reason' => 'Annulation de la commande',
    'type' => 'full',
]);

// Envoyer l'avoir
$api->creditNotes()->send($creditNoteId);

// Telecharger le PDF
$pdf = $api->creditNotes()->download($creditNoteId);
file_put_contents('avoir.pdf', $pdf);

use Scell\Sdk\Builders\InvoiceLineBuilder;
use Scell\Sdk\DTOs\Vat\BuyerContext;
use Scell\Sdk\DTOs\LineVatContext;
use Scell\Sdk\Enums\VatCategory;

// --- Mode 1 : buyer enregistre dans le registre ---
$resolution = $api->buyers()->vatContext(
    buyerOrInput: '019cb416-b6db-730c-b3a5-f8b7a4512eb1',
    line: ['category' => 'STANDARD'],
);
echo $resolution->rate;             // 0.0  (autoliquidation)
echo $resolution->category->value;  // 'REVERSE_CHARGE'
echo $resolution->en16931Code;      // 'AE'
echo $resolution->justification;    // "TVA non applicable, art. 259-1 du CGI"

// --- Mode 2 : buyer inline ---
$resolution = $api->buyers()->vatContext(
    buyerOrInput: new BuyerContext(
        country: 'DE',
        vatNumber: 'DE123456789',
        vatNumberValid: true,
    ),
    line: new LineVatContext(category: VatCategory::Standard),
);

// --- Override art. 259 A CGI (lieu de prestation force) ---
$resolution = $api->buyers()->vatContext(
    buyerOrInput: ['country' => 'DE', 'vat_number' => 'DE123456789'],
    line: ['category' => 'STANDARD', 'place_of_supply' => 'FR'],
);
echo $resolution->category->value;  // 'STANDARD' — 20 % TVA FR appliquee

// --- Builder de ligne avec categorie derivee ---
$line = (new InvoiceLineBuilder())
    ->withDescription('Logiciel SaaS')
    ->withQuantity(1)
    ->withUnitPrice(500.00)
    ->withCategory($resolution->category)      // derive le tax_rate + metadata
    ->withPlaceOfSupply('FR')                  // art. 259 A CGI
    ->build();
// $line['tax_rate']                          = 0.0
// $line['metadata']['category']              = 'REVERSE_CHARGE'
// $line['metadata']['exemption_reason']      = 'reverse_charge'
// $line['metadata']['place_of_supply']       = 'FR'

use Scell\Sdk\Laravel\Facades\ScellApi;
use Scell\Sdk\Laravel\Facades\Scell;
use Scell\Sdk\Laravel\Facades\ScellWebhook;

// Creer une facture (API Key)
$invoice = ScellApi::invoices()->builder()
    ->outgoing()
    ->facturX()
    // ...
    ->create();

// Consulter le solde (Bearer token)
$balance = Scell::balance()->get();

// Verifier un webhook
$payload = ScellWebhook::verify(
    request()->getContent(),
    request()->header('X-Scell-Signature')
);

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Scell\Sdk\Laravel\Facades\ScellWebhook;
use Scell\Sdk\Exceptions\ScellException;

class ScellWebhookController extends Controller
{
    public function handle(Request $request)
    {
        try {
            $payload = ScellWebhook::verify(
                $request->getContent(),
                $request->header('X-Scell-Signature')
            );
        } catch (ScellException $e) {
            return response()->json(['error' => 'Signature invalide'], 400);
        }

        $event = $payload['event'];
        $data = $payload['data'];

        match ($event) {
            'invoice.validated' => $this->handleInvoiceValidated($data),
            'invoice.transmitted' => $this->handleInvoiceTransmitted($data),
            'signature.completed' => $this->handleSignatureCompleted($data),
            'signature.refused' => $this->handleSignatureRefused($data),
            'balance.low' => $this->handleBalanceLow($data),
            default => null,
        };

        return response()->json(['received' => true]);
    }

    private function handleInvoiceValidated(array $data): void
    {
        // Traiter la facture validee
        $invoiceId = $data['id'];
        // ...
    }

    private function handleSignatureCompleted(array $data): void
    {
        // Telecharger le document signe
        $signatureId = $data['id'];
        $download = ScellApi::signatures()->download($signatureId, 'signed');
        // ...
    }
}

use Scell\Sdk\ScellApiClient;
use Scell\Sdk\Webhooks\WebhookVerifier;

class InvoiceService
{
    public function __construct(
        private readonly ScellApiClient $api
    ) {}

    public function createInvoice(Order $order): Invoice
    {
        return $this->api->invoices()->builder()
            ->outgoing()
            ->facturX()
            // ...
            ->create();
    }
}

// Creer un devis avec echeancier integre
$quote = $api->quotes()->builder()
    ->buyer('12345678901234', 'Client SA', new Address('1 rue Test', '75001', 'Paris'))
    ->line('Prestation conseil', 1, 5000.00, 20.0)
    ->withPaymentSchedule([
        ['amount_type' => 'percent', 'amount_value' => 30, 'due_date' => '2026-06-01'],
        ['amount_type' => 'percent', 'amount_value' => 70, 'milestone_label' => 'Livraison finale'],
    ])
    ->create();

// Acceder aux lignes de l'echeancier
$lines = $api->quotes()->paymentSchedule()->list($quote->id);
foreach ($lines as $line) {
    echo "{$line->order}. {$line->amountValue}% — {$line->status}\n";
}

// Remplacer entierement l'echeancier
$lines = $api->quotes()->paymentSchedule()->set($quote->id, [
    ['amount_type' => 'percent', 'amount_value' => 50, 'due_date' => '2026-07-01'],
    ['amount_type' => 'percent', 'amount_value' => 50, 'milestone_label' => 'Recette'],
]);

// Modifier une ligne specifique (PATCH partiel)
$lines = $api->quotes()->paymentSchedule()->patch($quote->id, [
    'update' => [['id' => $lineId, 'due_date' => '2026-07-15']],
]);

// Tracker financier (montants, avancement)
$summary = $api->quotes()->paymentSchedule()->summary($quote->id);
echo "Reste a facturer : {$summary->remaining} EUR ({$summary->percentInvoiced}% deja)\n";
if ($summary->isComplete()) {
    echo "Devis entierement facture.\n";
}

// Convertir une ligne en facture acompte
$depositInvoice = $api->quotes()->paymentSchedule()->convertLine($quote->id, $lineId, [
    'issue_date' => '2026-06-01',
]);

// Consulter les presets preconfigures (30/70, 3x mensuel, etc.)
$presets = $api->quotes()->paymentSchedule()->presets();

// Supprimer tout l'echeancier
$api->quotes()->paymentSchedule()->delete($quote->id);

// Consulter la configuration de marque du tenant
$branding = $api->branding()->getTenant();
if (!$branding->isReady()) {
    echo "Branding incomplet — les emails utilisent la marque Scell.io par defaut.\n";
}

// Mettre a jour le branding tenant
$branding = $api->branding()->updateTenant([
    'primary_color' => '#1a73e8',
    'email_footer' => 'Ma Societe SAS — SIRET 123 456 789 00010 — TVA FR12345678901',
    'email_signature' => "L'equipe Ma Societe",
]);

// Uploader un logo via URL presignee S3
$upload = $api->branding()->logoUploadUrlTenant('image/png');
// PUT $upload['url'] avec le binaire du logo
// Puis confirmer le logo_url via updateTenant(['logo_url' => $upload['public_url']])

// OU : upload DIRECT multipart en un seul appel (v3.4.0)
// Formats : jpeg, png, webp, svg/svgz. Max 2 Mo.
$branding = $api->branding()->uploadLogoTenant('/path/to/logo.png');
$branding = $api->branding()->uploadLogoSubTenant($subTenantId, '/path/to/logo.svg');

// Activer/desactiver le branding e-mail (v3.4.0)
// false = les e-mails sortent avec le branding par defaut du canal
$branding = $api->branding()->updateTenant(['brand_email_enabled' => false]);
// Pied de page calcule depuis la societe (lecture seule, utilise si email_footer vide)
echo $branding->computedEmailFooter;

// Branding d'un sub-tenant
$branding = $api->branding()->getSubTenant($subTenantId);
$branding = $api->branding()->updateSubTenant($subTenantId, [
    'primary_color' => '#e83e1a',
    'email_footer' => 'Mon Client SARL — SIRET 98765432109876',
]);

// URL presignee logo sub-tenant
$upload = $api->branding()->logoUploadUrlSubTenant($subTenantId, 'image/jpeg');

// Apercu de l'email brande AVANT tout envoi (rendu HTML par defaut)
// Ideal a injecter dans un <iframe srcdoc="..."> pour une previsualisation live.
$html    = $api->branding()->previewTenant();              // string (HTML)
$subHtml = $api->branding()->previewSubTenant($subTenantId);

// Apercu avec overrides NON persistes (v3.4.0) — previsualiser un branding
// avant de l'enregistrer
$html = $api->branding()->previewTenant([
    'brand_primary_color'   => '#e63946',
    'brand_email_footer'    => 'Footer de test',
    'brand_email_signature' => 'Signature de test',
    'brand_logo_url'        => 'https://cdn.example.com/logo.png',
]);

// Deriver les couleurs du template de facture par defaut depuis le logo e-mail (v3.4.0)
// 404 si aucun logo e-mail ; 422 si logo inaccessible ou couleurs trop neutres
$tpl = $api->invoiceTemplates()->deriveColorsFromEmailLogo();
echo "{$tpl->primaryColor} / {$tpl->accentColor}";

// Deriver la palette depuis le logo de FACTURE SANS persister (v3.5.0)
$palette = $api->invoiceTemplates()->deriveColorsFromInvoiceLogo();
echo "{$palette['primary_color']} / {$palette['accent_color']}";

// Apercu d'une facture-echantillon avec overrides de branding non persistes (v3.5.0)
$html = $api->invoiceTemplates()->preview(['primary_color' => '#0066FF']); // 'pdf' => binaire

// Groupes d'acomptes (deals multi-factures) (v3.5.0)
$groups = $api->invoices()->depositGroups(['has_no_balance' => true]);
$detail = $api->invoices()->depositGroup($groups[0]['id']); // 404 hors scope (anti-IDOR)

// Assistant de mentions legales de facture (v3.5.0)
$suggested = $api->invoiceMentions()->assistant(['vat_profile' => 'franchise_base']);
$preview   = $api->invoiceMentions()->preview(['company' => ['name' => 'ACME']]);

// Apercu HTML non persiste d'un document en cours de saisie (v3.4.0)
// Rendu avec le vrai template + branding + mentions de la Company emettrice
$html = $api->documents()->preview([
    'type' => 'invoice', // 'invoice' | 'credit_note' | 'quote'
    'buyer' => ['name' => 'Client SA'],
    'lines' => [
        ['description' => 'Prestation', 'quantity' => 1, 'unit_price' => 1000.00, 'tax_rate' => 20.0],
    ],
]);

// Envoyer une facture par email (utilise le branding tenant si isReady())
$result = $api->invoices()->sendByEmail($invoiceId, [
    'email'   => '[email protected]',
    'subject' => 'Votre facture FA-2026-0042',
    'message' => 'Veuillez trouver ci-joint votre facture.',
]);
echo "Email envoye a {$result['recipient']} le {$result['sent_at']}\n";

use Scell\Sdk\Exceptions\ScellException;
use Scell\Sdk\Exceptions\ValidationException;
use Scell\Sdk\Exceptions\AuthenticationException;
use Scell\Sdk\Exceptions\RateLimitException;

try {
    $invoice = $api->invoices()->create([...]);
} catch (ValidationException $e) {
    // Erreurs de validation
    foreach ($e->getErrors() as $field => $messages) {
        echo "$field: " . implode(', ', $messages);
    }
} catch (AuthenticationException $e) {
    // API Key invalide
    echo "Authentification echouee: {$e->getMessage()}";
} catch (RateLimitException $e) {
    // Limite de requetes atteinte
    $retryAfter = $e->getRetryAfter();
    echo "Reessayez dans {$retryAfter} secondes";
} catch (ScellException $e) {
    // Autre erreur API
    echo "Erreur: {$e->getMessage()}";
    echo "Code: {$e->getScellCode()}";
}

use Scell\Sdk\Exceptions\QuoteNotEditableException;
use Scell\Sdk\Exceptions\ScheduleLineAlreadyInvoicedException;
use Scell\Sdk\Exceptions\ScheduleSumExceedsTotalException;
use Scell\Sdk\Exceptions\BuyerHasNoEmailException;
use Scell\Sdk\Exceptions\InvoiceBrandingIncompleteException;

try {
    $invoice = $api->quotes()->paymentSchedule()->convertLine($quoteId, $lineId);
} catch (QuoteNotEditableException $e) {
    // Devis signe/accepte : impossible de modifier l'echeancier (409)
} catch (ScheduleLineAlreadyInvoicedException $e) {
    // Cette ligne a deja ete convertie en facture (422)
} catch (ScheduleSumExceedsTotalException $e) {
    // La somme des lignes depasse le total TTC du devis (422)
}

try {
    $result = $api->invoices()->sendByEmail($invoiceId);
} catch (BuyerHasNoEmailException $e) {
    // L'acheteur n'a pas d'adresse email dans le registre (422)
} catch (InvoiceBrandingIncompleteException $e) {
    // Le branding tenant est incomplet — passer force_branding: false (422)
}

use Scell\Sdk\Enums\Direction;
use Scell\Sdk\Enums\OutputFormat;
use Scell\Sdk\Enums\InvoiceStatus;
use Scell\Sdk\Enums\SignatureStatus;
use Scell\Sdk\Enums\AuthMethod;
use Scell\Sdk\Enums\WebhookEvent;
use Scell\Sdk\Enums\Environment;
use Scell\Sdk\Enums\RejectionCode;
use Scell\Sdk\Enums\DisputeType;

// Direction de facture
Direction::Outgoing; // Vente
Direction::Incoming; // Achat

// Format de sortie
OutputFormat::FacturX; // Factur-X PDF/A-3
OutputFormat::UBL;     // UBL 2.1
OutputFormat::CII;     // UN/CEFACT CII

// Methode d'authentification
AuthMethod::Email; // OTP par email
AuthMethod::Sms;   // OTP par SMS
AuthMethod::Both;  // Email + SMS

// Codes de rejet (factures entrantes)
RejectionCode::IncorrectAmount; // Montant incorrect
RejectionCode::Duplicate;       // Facture en double
RejectionCode::UnknownOrder;    // Commande inconnue
RejectionCode::IncorrectVat;    // TVA incorrecte
RejectionCode::Other;           // Autre

// Types de litige (factures entrantes)
DisputeType::AmountDispute;     // Litige sur le montant
DisputeType::QualityDispute;    // Litige sur la qualite
DisputeType::DeliveryDispute;   // Litige sur la livraison
DisputeType::Other;             // Autre

// Statut de facture
InvoiceStatus::Paid;  // Facture payee

// Evenements webhook
WebhookEvent::InvoiceValidated;
WebhookEvent::InvoiceIncomingReceived;
WebhookEvent::InvoiceIncomingAccepted;
WebhookEvent::InvoiceIncomingPaid;
WebhookEvent::SignatureCompleted;
WebhookEvent::BalanceLow;

use Scell\Sdk\Config;
use Scell\Sdk\ScellApiClient;

$config = new Config(
    baseUrl: 'https://api.scell.io/api/v1',
    timeout: 60,
    connectTimeout: 15,
    retryAttempts: 5,
    retryDelay: 200,
    verifySsl: true,
    webhookSecret: 'whsec_...',
);

$api = ScellApiClient::withApiKey('tk_live_...', $config);

use Scell\Sdk\ScellTenantClient;

// Create client with tenant key
$tenant = ScellTenantClient::create('tk_live_...');

// Sandbox mode
$tenant = ScellTenantClient::sandbox('tk_test_...');

// Profile management
$profile = $tenant->me();
$tenant->update(['company_name' => 'New Name']);
$balance = $tenant->balance();
$stats = $tenant->stats();
$result = $tenant->regenerateKey();

// Sub-Tenants
$subTenants = $tenant->subTenants()->list();
$sub = $tenant->subTenants()->create([...]);

// Direct Invoices (without sub-tenant scope)
$invoices = $tenant->directInvoices()->list();
$invoice = $tenant->directInvoices()->create([...]);
$tenant->directInvoices()->bulkCreate([...]);
$tenant->directInvoices()->bulkSubmit([...]);

// Direct Credit Notes
$notes = $tenant->directCreditNotes()->list();
$note = $tenant->directCreditNotes()->create([...]);

// Per sub-tenant invoices
$subInvoices = $tenant->invoices()->listForSubTenant($subId);
$invoice = $tenant->invoices()->createForSubTenant($subId, [...]);

// Incoming invoices
$incoming = $tenant->incomingInvoices()->listForSubTenant($subId);
$tenant->incomingInvoices()->accept($invoiceId);

// Fiscal compliance (ISCA)
$compliance = $tenant->fiscal()->compliance();
$integrity = $tenant->fiscal()->integrity();
$attestation = $tenant->fiscal()->attestation(2025);

// Billing
$billingInvoices = $tenant->billing()->invoices();
$usage = $tenant->billing()->usage();

// Detailed stats
$overview = $tenant->detailedStats()->overview();
bash
php artisan vendor:publish --tag=scell-config