Search by

lanka / skyttelpass-sdk

Standalone PHP client for all-agreement SkyttelPASS toll and ferry charges

Maintainers

Package info

github.com/ivashchukk/skytellpass-sdk

pkg:composer/lanka/skyttelpass-sdk

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-09-05 19:55 UTC

This package is auto-updated.

Last update: 2026-09-06 08:05:50 UTC


README

Standalone PHP 8.3+ client for reading road toll and ferry charges from every SkyttelPASS agreement available to a phone login, covering all vehicles. No customer number, vehicle selection, Laravel, or browser is required.

This is an unofficial client for the portal's private web endpoints. It covers charges billed through SkyttelPASS, including ferry journeys visible there. Separate AutoPASS for ferje and Moss–Horten portals are outside its scope.

Install locally

The package is not published. To use this checkout from another Composer project:

composer config repositories.skyttelpass path ../skyttelpass-sdk
composer require 'lanka/skyttelpass-sdk:@dev'

Fetch all agreements

use Skyttelpass\Sdk\Credentials;
use Skyttelpass\Sdk\SkyttelpassClient;

$client = new SkyttelpassClient(new Credentials(
    phoneNumber: $_ENV['SKYTTELPASS_PHONE'],
    countryPrefix: $_ENV['SKYTTELPASS_PREFIX'], // e.g. +48
    password: $_ENV['SKYTTELPASS_PASSWORD'],
));

$result = $client->fetchCharges(from: '2026-01-01', to: '2026-09-05');

foreach ($result->charges as $charge) {
    // Upsert by $charge->id; keep the amount as a decimal string.
}

if (!$result->isComplete()) {
    foreach ($result->failures as $failure) {
        // Retry the reported coverage later. Do not advance a global sync cursor.
    }
}

Each call discovers the current agreement set and fetches inclusive calendar dates in windows of at most 31 days. It selects agreements sequentially, verifies the active customer number, and queries without vehicle or invoice filters. Historical charges remain included even when a vehicle is no longer on the current vehicle list. Cookies remain in memory for the operation; the next fetch starts fresh. Do not use one client concurrently.

SkyttelpassClient accepts an optional Guzzle ClientInterface as its second constructor argument for tests and custom transports. The normal Guzzle cookie middleware must be present. Credentials are sent only to the fixed https://skyttelpass.no origin; redirects are disabled. Defaults are a 10-second connection timeout and 30-second request timeout.

Records

FetchResult contains from, to, agreements, charges, failures, and isComplete().

Agreement contains string number and nullable name. A single-agreement login may not supply a display name.

Charge is immutable:

Field Meaning
id Stable agreementNumber:passageId key
passageId, agreementNumber Original identifiers, preserved as strings
vehicleRegistration, tagNumber Nullable source identifiers
operator, station Operator and toll station/ferry route
occurredAtLocal YYYY-MM-DD HH:MM:SS, local wall-clock time without an assumed UTC offset; source precision is minutes
netAmount, vatAmount, grossAmount Exact decimal strings, e.g. "1233.90"
currency NOK, as specified by the portal
raw Original source row; contains private account activity

Both toll and ferry records are returned. The source does not provide a reliable expense-category field, so categorization belongs to the caller. Zero-cost records and negative adjustments are retained. No VAT rate or missing timezone is guessed. Repeated identical source rows are deduplicated; conflicting rows with the same stable ID fail the affected window.

Incomplete results and errors

Initial authentication or agreement-discovery failure throws SdkException. Its category and optional retryAfterSeconds are safe to inspect; HTTP failures also expose the status through getCode(). Invalid credentials or date syntax throw InvalidArgumentException before requests.

Once agreements are known, an agreement-specific failure preserves previously successful windows and continues with other agreements. The failed window and the rest of that agreement's requested range are recorded as incomplete. Windows are validated before any of their records are returned.

FetchFailure provides agreementNumber, inclusive from/to, category, a sanitized message, and nullable retryAfterSeconds. Categories include authentication, session_expired, agreement_mismatch, rate_limit, stale_action, transport, http, protocol, and invalid_data.

  • Session expiry triggers one fresh login and reselection for the interrupted window.
  • A recognized stale action triggers one page reload and action rediscovery.
  • HTTP 429 stops further requests and records every remaining agreement range as incomplete.
  • Other network/server errors are left for caller-managed retry; this package never sleeps or schedules jobs.
  • Only the verified Ingen transaksjoner funnet error is accepted as an empty result. Unknown errors are failures.

isComplete() means all requested windows for the discovered agreement set were read and decoded successfully. It cannot certify records the provider has not yet posted or detect an undocumented silent server-side cap. The portal currently returns arrays without a pagination cursor. Applications should refetch an overlapping historical period when syncing, because provider data can change.

No credentials, cookies, or response bodies are logged by the SDK. Keep raw records private and do not enable Guzzle debug/history logging against live credentials. Persistent credential/session storage, database writes, expense classification, PDF downloads, receipt emails, and scheduling are deliberately left to the caller.

Development and live smoke test

composer install
composer validate --strict
composer test

Offline tests use synthetic portal responses and Guzzle's mock handler; no real account credentials or responses are stored in this repository.

To perform an opt-in read-only live check, supply SKYTTELPASS_PHONE, SKYTTELPASS_PREFIX, and SKYTTELPASS_PASSWORD through your environment or secret manager, then run:

php bin/skyttelpass-smoke-test --from=2026-08-01 --to=2026-08-31

The command prints aggregate counts and sanitized failure categories, never records, account identifiers, or credentials. Exit codes: 0 complete, 1 incomplete, 2 invalid invocation or initial failure. No receipt/email actions are called.

License

Proprietary. No redistribution license is granted.