Looking to hire Laravel developers? Try LaraJobs

laravel-snelstart maintained by jitso

Description
Laravel package for the Snelstart B2B API v2 with Eloquent-like models
Author
Last update
2026/04/03 15:33 (dev-master)
License
Links
Downloads
149

Comments
comments powered by Disqus

Laravel Snelstart

A Laravel package for the Snelstart B2B API v2 with an Eloquent-like interface. Query, create, update, and delete Snelstart resources using familiar Laravel syntax.

Requirements

  • PHP 8.2+
  • Laravel 11 or 12

Installation

composer require jitso/laravel-snelstart

The service provider and facade are auto-discovered.

Configuration

Publish the config file:

php artisan vendor:publish --tag=snelstart-config

Add the following to your .env:

SNELSTART_SUBSCRIPTION_KEY=your-subscription-key
SNELSTART_CLIENT_KEY=your-oauth2-client-id
SNELSTART_CLIENT_SECRET=your-oauth2-client-secret

Authentication type

The package supports two authentication methods. Set SNELSTART_AUTH_TYPE in your .env:

OAuth2 (default) -- uses client_credentials grant with client key + secret:

SNELSTART_AUTH_TYPE=oauth
SNELSTART_CLIENT_KEY=your-client-id
SNELSTART_CLIENT_SECRET=your-client-secret

Client Key -- uses the simpler clientkey grant with only a client key:

SNELSTART_AUTH_TYPE=clientkey
SNELSTART_CLIENT_KEY=your-client-key

All config options

Key Env variable Default
subscription_key SNELSTART_SUBSCRIPTION_KEY
authentication_type SNELSTART_AUTH_TYPE oauth
client_key SNELSTART_CLIENT_KEY
client_secret SNELSTART_CLIENT_SECRET — (only for oauth)
base_url SNELSTART_BASE_URL https://b2bapi.snelstart.nl/v2
token_url SNELSTART_TOKEN_URL https://auth.snelstart.nl/b2b/token
cache_token SNELSTART_CACHE_TOKEN true

Usage

Basic CRUD

use Jitso\LaravelSnelstart\Models\Artikel;

// Get all
$artikelen = Artikel::all();

// Find by ID
$artikel = Artikel::find('550e8400-e29b-41d4-a716-446655440000');

// Create
$artikel = Artikel::create([
    'artikelcode' => 'PROD-001',
    'omschrijving' => 'Voorbeeld product',
    'verkoopprijs' => 29.95,
]);

// Update
$artikel->update(['omschrijving' => 'Nieuwe omschrijving']);

// Delete
$artikel->delete();

Query builder (OData)

Resources with OData support can be queried with a familiar builder syntax. Operators are automatically translated to OData filter expressions.

use Jitso\LaravelSnelstart\Models\Relatie;

// Where clause (translates to $filter=naam eq 'Jitso')
$relaties = Relatie::where('naam', 'Jitso')->get();

// Comparison operators
$relaties = Relatie::where('krediettermijn', '>', 30)->get();

// Contains / starts with / ends with
$relaties = Relatie::query()
    ->whereContains('naam', 'holding')
    ->get();

// Pagination with $top and $skip
$relaties = Relatie::take(10)->skip(20)->get();

// Get the first result
$relatie = Relatie::where('naam', 'Jitso')->first();

// Combine multiple filters (joined with 'and')
$artikelen = Artikel::where('isNonActief', false)
    ->where('verkoopprijs', '>', 10)
    ->take(25)
    ->get();

// Raw OData filter
$artikelen = Artikel::filter("contains(omschrijving, 'test')")->get();

// Lazy pagination (automatically fetches all pages)
$alleRelaties = Relatie::query()->paginate(500);

Supported operators: =, !=, >, >=, <, <=, or pass OData operators directly (eq, ne, gt, ge, lt, le).

firstOrCreate / updateOrCreate

Find-or-create and upsert patterns, just like Eloquent:

use Jitso\LaravelSnelstart\Models\Relatie;
use Jitso\LaravelSnelstart\Models\Artikel;

// Find by attributes, or create with extra data
$relatie = Relatie::firstOrCreate(
    ['naam' => 'Jitso B.V.'],
    ['email' => 'info@jitso.nl', 'telefoon' => '0612345678'],
);

// Find by attributes, update if found, create if not
$artikel = Artikel::updateOrCreate(
    ['artikelcode' => 'PROD-001'],
    ['omschrijving' => 'Updated product', 'verkoopprijs' => 39.95],
);

// Get an unsaved instance if not found (useful for forms)
$artikel = Artikel::firstOrNew(['artikelcode' => 'NIEUW']);
$artikel->omschrijving = 'Handmatig ingevuld';
$artikel->save();

// Find by ID with fallback to empty instance
$artikel = Artikel::findOrNew('possibly-invalid-uuid');

// Also works via the query builder
$relatie = Relatie::where('naam', 'Jitso B.V.')
    ->firstOrCreate(['email' => 'info@jitso.nl']);

$artikel = Artikel::where('artikelcode', 'PROD-001')
    ->updateOrCreate(['verkoopprijs' => 49.95]);

Sub-resources

Some models expose related resources as methods:

$relatie = Relatie::find('uuid');

$inkoopboekingen = $relatie->inkoopboekingen();
$verkoopboekingen = $relatie->verkoopboekingen();
$machtigingen = $relatie->doorlopendeIncassomachtigingen();

// Custom fields (read & update)
$fields = $relatie->customFields();
$relatie->updateCustomFields([
    ['name' => 'MijnVeld', 'value' => 'waarde'],
]);
$artikel = Artikel::find('uuid');

$fields = $artikel->customFields();
$artikel->updateCustomFields([...]);

// Article price agreements
$afspraken = Artikel::prijsafspraken();
$afspraken = Artikel::prijsafsprakenQuery()
    ->where('artikelCode', 'PROD-001')
    ->get();

Special models

CompanyInfo -- singleton resource:

use Jitso\LaravelSnelstart\Models\CompanyInfo;

$info = CompanyInfo::get();
$info->update(['bedrijfsnaam' => 'Nieuwe Naam B.V.']);

Rapportage:

use Jitso\LaravelSnelstart\Models\Rapportage;

$balans = Rapportage::kolommenbalans();
$periode = Rapportage::periodebalans(['boekjaar' => 2025]);

Documents:

use Jitso\LaravelSnelstart\Models\Document;

$doc = Document::find('uuid');
$docs = Document::forParent('VerkoopBoeking', 'parent-uuid');
$doc = Document::createForType('VerkoopBoeking', [
    'parentIdentifier' => 'parent-uuid',
    'fileName' => 'factuur.pdf',
    'content' => base64_encode($pdfContent),
]);

Verkooporder processtatus:

use Jitso\LaravelSnelstart\Models\Verkooporder;

$order = Verkooporder::find('uuid');
$order->updateProcesStatus('Uitgevoerd');

Inkoopboeking -- UBL & attachment import:

use Jitso\LaravelSnelstart\Models\Inkoopboeking;

Inkoopboeking::createFromUbl('factuur.xml', $xmlContent);
Inkoopboeking::createFromAttachment('scan.pdf', base64_encode($pdf));

BTW aangifte:

use Jitso\LaravelSnelstart\Models\BtwAangifte;

$aangifte = BtwAangifte::find('uuid');
$aangifte->externAangeven(true);

Verkoopfactuur -- UBL export:

use Jitso\LaravelSnelstart\Models\Verkoopfactuur;

$factuur = Verkoopfactuur::find('uuid');
$ubl = $factuur->ubl();

Using the Facade

For direct API access without models:

use Jitso\LaravelSnelstart\Facades\Snelstart;

$response = Snelstart::get('artikelen', ['$top' => 10]);
$response = Snelstart::post('artikelen', ['artikelcode' => 'NEW']);
$response = Snelstart::put('artikelen/uuid', [...]);
Snelstart::delete('artikelen/uuid');

Validation

Write models validate attributes at runtime. Each model defines which fields are $fillable (allowed) and which are $required (mandatory on create). A ValidationException is thrown when constraints are violated.

use Jitso\LaravelSnelstart\Models\Verkoopboeking;
use Jitso\LaravelSnelstart\Exceptions\ValidationException;

try {
    // Missing required fields → ValidationException
    Verkoopboeking::create([
        'omschrijving' => 'Test',
    ]);
} catch (ValidationException $e) {
    // "Verkoopboeking: missing required fields: factuurnummer, klant, boekingsregels"
    $e->errors; // ['factuurnummer' => ['This field is required.'], ...]
}

try {
    // Unknown field → ValidationException
    Verkoopboeking::create([
        'factuurnummer' => 'F-001',
        'klant' => ['id' => '...'],
        'boekingsregels' => [...],
        'nietBestaandVeld' => 'oops',
    ]);
} catch (ValidationException $e) {
    // "Verkoopboeking: unknown fields: nietBestaandVeld"
}

Validation runs on both create() and update(). The update() method only checks fillable (not required), since partial updates are common.

All models expose their fields via @property PHPDoc annotations, so your IDE will autocomplete available field names when constructing arrays for create() and update().

Error handling

The package throws specific exceptions based on HTTP status codes:

use Jitso\LaravelSnelstart\Exceptions\AuthenticationException;
use Jitso\LaravelSnelstart\Exceptions\NotFoundException;
use Jitso\LaravelSnelstart\Exceptions\ValidationException;
use Jitso\LaravelSnelstart\Exceptions\SnelstartException;

try {
    $artikel = Artikel::find('non-existent-uuid');
} catch (NotFoundException $e) {
    // 404
} catch (ValidationException $e) {
    // 400 or local validation -- access field errors via $e->errors
} catch (AuthenticationException $e) {
    // 401 / 403
} catch (SnelstartException $e) {
    // Any other API error
}

Available models

Each model only exposes the methods its API endpoint actually supports. Your IDE autocompletion will only show relevant methods.

Capability Traits Methods
Read CanRead all(), find(), findOrNew(), query(), where(), take(), skip(), filter()
Create CanCreate create(), firstOrCreate(), firstOrNew()
Update CanUpdate update()
Delete CanDelete delete()
Upsert CanUpsert updateOrCreate()

Full CRUD + OData

Artikel, Bankboeking, Kasboeking, Relatie, Verkoopboeking, Verkooporder, Offerte

Full CRUD

Kostenplaats, Inkoopboeking, Memoriaalboeking

Read + Create

Grootboek

Read-only + OData

GrootboekMutatie, Inkoopfactuur, Prijsafspraak, ArtikelPrijsafspraak, Verkoopfactuur, BtwAangifte, Actieprijzen, VatRate, VatRateDefinition

Read-only

ArtikelOmzetgroep, Dagboek, Land, Verkoopordersjabloon, BtwTarief

Special

CompanyInfo, Document, Rapportage, Bankafschriftbestand, Authorization, Echo

License

MIT