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.
// 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();
}
}
// 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);