Search by

linkado-ru / laravel

everully

Laravel integration for the Linkado affiliate platform.

Package info

github.com/linkado-ru/laravel

pkg:composer/linkado-ru/laravel

Statistics

Installs: 188

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

v3.0.0 2026-10-27 08:10 UTC

This package is auto-updated.

Last update: 2026-11-11 05:19:00 UTC


README

Laravel 13 integration for the Linkado affiliate platform. The package captures referral attribution, creates Linkado SSO links, and delivers official Linkado SDK events through a transactional outbox.

Requirements

  • PHP 8.3 or later
  • Laravel 13
  • Linkado PHP SDK ^1.1 (resolved by Composer)
  • a queue worker for live event delivery
  • Laravel's scheduler for automatic recovery and attribution pruning

Installation

Install the package through Composer:

composer require linkado-ru/laravel
php artisan linkado:install
php artisan migrate

linkado:install publishes configuration, migrations, and translations. It is idempotent and does not run migrations. Published migration filenames remain stable, so later installs and publication do not create duplicate migrations. Use php artisan linkado:install --force only when you intend to overwrite already-published files.

The equivalent publish tags are:

php artisan vendor:publish --tag=linkado
php artisan vendor:publish --tag=linkado-config
php artisan vendor:publish --tag=linkado-migrations
php artisan vendor:publish --tag=linkado-lang

Configuration

A production installation using a standard via / 60-day program needs two environment values after migrations, host adapters/producers, workers and scheduler are installed:

LINKADO_TOKEN=integration-credential
LINKADO_PROGRAM_KEY=program-public-key

The default is live; tracking, customer, billing and refund features are on, while SSO remains off. Without either credential, the application boots normally but live capture, hosted markup, recording and SSO are unavailable; no connector or HTTP request is created. linkado:health and linkado:diagnose report setting names without credential values. Offline health cannot confirm remote program settings or credential scopes.

For local delivery, add LINKADO_URL=https://linkado.test. Known URLs are resolved locally: API {origin}/api/v1, script {origin}/build/tracking.js, clicks {API base}/tracking/clicks. No discovery request is made. A nonempty LINKADO_URL wins over legacy LINKADO_BASE_URL, whose meaning remains a full API URL including any custom prefix; diagnose warns that the legacy value is ignored. Both are validated even when one loses precedence. Blank/null optional URLs use defaults; invalid nonempty URLs fail with the setting name and never switch to production.

Origins require HTTPS and a host, allow a port and trailing root slash, and forbid API paths, query, fragment, user-info and unsafe characters. Advanced script/endpoint overrides affect only their corresponding URL, support CDN/proxy hosts and query strings, and permit HTTP only in local/testing. API and SSO always require HTTPS. SSO redirects must match the selected API host and effective port; CDN/proxy overrides grant no SSO permission.

Local shadow needs only LINKADO_MODE=shadow and a public program key (optionally LINKADO_URL). It records local immutable event snapshots, but does not verify or capture new visits, load the hosted script, or make SSO/outbox HTTP requests. LINKADO_MODE=off stops new capture and recording. Consume-before-claim still applies in every mode. SSO requires an explicit LINKADO_SSO_ENABLED=true and a host user resolver.

Never commit credentials. A default URL does not prove DNS/TLS, public assets, merchant CORS, workers, program rules or remote scopes are ready.

Every package configuration key is listed below. Values without an environment variable are intentionally changed in config/linkado.php after publishing.

Configuration key Environment variable / default Purpose
linkado.mode LINKADO_MODE=live Delivery mode: off, shadow, or live.
linkado.connection LINKADO_DB_CONNECTION=null Database connection that owns the host transaction and Linkado tables. null uses the default connection.
linkado.queue LINKADO_QUEUE=null Queue name for delivery jobs. null uses the connection's default queue.
linkado.url LINKADO_URL=null Optional HTTPS origin; default https://app.linkado.ru.
linkado.base_url LINKADO_BASE_URL=null Legacy full HTTPS API URL; lower priority than origin.
linkado.token LINKADO_TOKEN=null Bearer credential used only when an SDK request is made.
linkado.program_key LINKADO_PROGRAM_KEY=null Public Linkado program key included in events and SSO requests.
linkado.features.sso LINKADO_SSO_ENABLED=false Enables SSO launches.
linkado.features.tracking LINKADO_TRACKING_ENABLED=true Enables tracking rendering and attribution capture.
linkado.features.customer_events LINKADO_CUSTOMER_EVENTS_ENABLED=true Enables customer-created and lead-created events.
linkado.features.billing_events LINKADO_BILLING_EVENTS_ENABLED=true Enables payment and subscription events.
linkado.features.refund_events LINKADO_REFUND_EVENTS_ENABLED=true Enables payment-refunded events.
linkado.tracking.script_url LINKADO_TRACKING_SCRIPT_URL=null (resolved origin/build/tracking.js) Hosted tracking script URL. Required when tracking is rendered.
linkado.tracking.endpoint_url LINKADO_TRACKING_ENDPOINT_URL=null (resolved API/tracking/clicks) Hosted tracking endpoint exposed to the script. Required when tracking is rendered.
linkado.tracking.referral_parameter LINKADO_REFERRAL_PARAMETER=via Referral query-string parameter.
linkado.tracking.visitor_cookie linkado_visitor Encrypted package visitor cookie name.
linkado.tracking.click_cookie lk_click Hosted script click-cookie name.
linkado.tracking.referral_cookie lk_referral Hosted script referral-cookie name.
linkado.tracking.ttl_seconds LINKADO_ATTRIBUTION_TTL_SECONDS=5184000 Requested browser attribution window in seconds; pending expiry comes from the authenticated resolver.
linkado.sso.route linkado.sso.launch Name of the package's SSO POST route.
linkado.sso.middleware web, auth, throttle:6,1 Middleware protecting SSO launches.
linkado.sso.error_redirect / Local path after an SSO failure; unsafe values fall back to /.
linkado.delivery.max_attempts 8 Maximum automatic delivery attempts.
linkado.delivery.claim_timeout_seconds 600 Time before an in-progress delivery claim is stale.
linkado.delivery.base_delay_seconds 60 Initial retry delay.
linkado.delivery.max_delay_seconds 21600 Maximum retry delay.
linkado.delivery.retry_window_seconds 86400 Maximum event age for automatic retries.

Delivery modes and features

LINKADO_MODE is an operational safety switch:

  • off returns null without constructing an SDK event or writing a package row.
  • shadow stores an immutable, terminal local snapshot and never dispatches HTTP delivery.
  • live stores a pending event and dispatches its queue job only after the host transaction commits.

Linkado::enabled(LinkadoFeature $feature): bool checks the package flag, mode and configuration without host policy, database access or HTTP. Use it before application policy/UI decisions. Shadow recording requires a program key and does not require a token; SSO requires live. Package operations use the same readiness check. Feature flags are evaluated after the SDK DTO identifies its event family. The default eligibility policy allows enabled features. Applications can replace it with the DeterminesLinkadoEligibility contract:

<?php

namespace App\Linkado;

use Linkado\Laravel\Contracts\DeterminesLinkadoEligibility;
use Linkado\Laravel\Enums\LinkadoFeature;
use Linkado\Laravel\Support\EligibilityContext;

final class LinkadoEligibility implements DeterminesLinkadoEligibility
{
    public function allows(LinkadoFeature $feature, EligibilityContext $context): bool
    {
        return ! app()->environment('testing');
    }
}

Bind it in an application service provider:

use App\Linkado\LinkadoEligibility;
use Linkado\Laravel\Contracts\DeterminesLinkadoEligibility;

$this->app->singleton(
    DeterminesLinkadoEligibility::class,
    LinkadoEligibility::class,
);

Eligibility exceptions fail closed: the package does not write or deliver the event.

Attribution and event recording

Apply the linkado.attribution middleware to public landing routes that should capture Linkado click or referral values:

use Illuminate\Support\Facades\Route;

Route::middleware(['web', 'linkado.attribution'])->group(function (): void {
    Route::get('/pricing', fn () => view('pricing'));
    Route::get('/register', fn () => view('auth.register'));
});

The package manages its encrypted visitor cookie on the web middleware group and stores only its SHA-256 hash. Attribution always uses the last server-verified visit before registration. Each newer confirmed click, including another visit from the same partner, replaces the unconsumed candidate and uses that click's server expiry. Reading the same cookie again never extends its window. Ordinary page views, invalid links and slug-only values do not establish or refresh attribution.

Verified visits

Install PHP SDK ^1.1, deploy the authenticated Linkado resolver, and apply all five published migrations, including 2026_10_20_075845_add_visit_order_to_linkado_pending_attributions_table.php. No strategy flag or environment variable is required. Existing rows are not remotely verified or backfilled by migration. Unverified rows cannot be consumed merely because their cookies match.

The atomic lk_attribution v1 cookie takes precedence over lk_click/lk_referral; malformed or foreign-program snapshots never fall back to the pair or a query slug. Legacy click cookies can be verified when the atomic cookie is absent. Slug-only/query captures are unavailable: wait for the hosted script's confirmed click. Browser sequence, expiry and IDs are untrusted. The package resolves click/program/slug/order/creation/expiry via authenticated POST tracking/clicks/resolve using the integration's existing events:write ability. No raw cookie or fingerprint is sent.

The verified click identifies its original participation even if the partner renamed the slug after the visit. A syntactically valid browser slug need not equal the current resolved slug. Unverified stored clicks cannot inherit another visit's verification metadata.

Call Linkado::attribution()->prepare($request) before the host registration transaction, after resolving eligibility/anonymous identity. The capture middleware prepares outside its own transaction. Verification uses one request with a one-second connection/two-second total timeout, without retry. Shadow/off/denied tracking does not verify. An outage or invalid response preserves pending state and normal signup; a matching previously verified click may still be consumed before expiry. Verification is request-local, bound to unchanged source cookies/program, and is never performed inside an active package connection transaction.

Inside the host transaction, consume applies the prepared candidate under host-identity then pending-row locks. A larger server-issued visit order replaces only an unconsumed, same-identity candidate and uses the server's absolute expiry. A repeat read of the same click cannot extend it. A newer click from the same partner starts its own refreshed window. A stale validated request cannot overwrite a newer stored candidate; consumption uses that newest verified row. Invalid mismatches return no attribution and scrub only the owned row into a consumed marker. The host identity must close on claim, including organic/disabled recording paths, preventing capture after pruning or visitor rotation. Freeze the returned value in the outbox with the registration fact; delivery never reads cookies again.

The snapshot is linearized when registration acquires the identity/pending lock. Only completed browser click snapshots and already captured verified visits are available: a fetch whose result has not reached the browser/server capture before that point cannot be reconstructed. Browser navigation, supported Web Locks clients, mixed-version rollout and actual hosted/registration acceptance require separate testing; source overlays do not establish installed release behavior.

The following transaction example is shared by both strategies. For the verified strategy, call Linkado::attribution()->prepare($request) before entering it.

Consume attribution and record the related SDK event inside the same transaction and on the connection configured by linkado.connection:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Linkado\Laravel\Facades\Linkado;
use Linkado\PhpSdk\DataObjects\CustomerCreatedEventData;

Linkado::attribution()->prepare($request);

DB::connection(config('linkado.connection'))->transaction(
    function () use ($request, $customer): void {
        /** @var Request $request */
        $attribution = Linkado::attribution()->consume($request);

        Linkado::record(
            sourceKey: 'customer-created:'.$customer->getKey(),
            eventFactory: fn (string $eventId): CustomerCreatedEventData => new CustomerCreatedEventData(
                event_id: $eventId,
                program_key: (string) config('linkado.program_key'),
                occurred_at: $customer->created_at,
                external_customer_id: (string) $customer->getKey(),
                click_id: $attribution?->clickId,
                referral_slug: $attribution?->clickId === null
                    ? $attribution?->referralSlug
                    : null,
            ),
        );
    },
);

Successful consumption returns attribution once and clears its click/referral values. A consumed marker remains until the original expiry, preventing another capture in that window; the daily prune removes expired markers. Consumption and its after-commit event roll back with the caller transaction. No migration is required for these storage semantics.

Missing, malformed, or mismatched source cookies return null and discard attribution for the identified visitor, retaining the same scrubbed marker until expiry. Discard rolls back with the caller and emits no AttributionConsumed event. An invalid or unknown visitor cookie does not alter another visitor's row. Existing integrations must supply matching source cookies; visitor-only consumption no longer returns attribution.

The source key is the application's idempotency key. Repeating the same source key and payload returns the original row; reusing it for a different payload preserves the first row and emits a critical diagnostic event. Use the original business timestamp for occurred_at, not the current time on each invocation. If the application repeats recording, preserve the original attribution and other payload fields from its business fact; consumption itself returns attribution only once. Do not put email addresses, phone numbers, credentials, or other secrets in source keys.

Use the official linkado-ru/php-sdk DTOs for all supported event types:

  • CustomerCreatedEventData and LeadCreatedEventData
  • PaymentSucceededEventData
  • SubscriptionRenewedEventData and SubscriptionCancelledEventData
  • PaymentRefundedEventData

Amounts are positive integers in minor currency units. Never construct a package-specific replacement DTO, generate your own event ID inside the factory, or call Linkado HTTP APIs inside the host transaction.

Tracking

With live credentials, tracking uses the resolved URLs by default. Render the directive once in the page layout. Optional CDN/proxy overrides are:

LINKADO_TRACKING_SCRIPT_URL=https://cdn.example.test/linkado.js
LINKADO_TRACKING_ENDPOINT_URL=https://tracking.example.test/events
<!doctype html>
<html lang="en">
    <head>
        @linkadoTracking
    </head>
    <body>
        {{ $slot }}
    </body>
</html>

The directive renders nothing outside live, without credentials, when tracking is disabled, or when eligibility denies the request. Its two URLs must use HTTPS outside local/testing environments.

The script element keeps defer and emits data-endpoint, data-program-key, data-referral-param, and data-attribution-window-days. Compatibility aliases data-endpoint-url and data-referral-parameter contain the same endpoint and referral parameter. Values are HTML-escaped; the API token is never rendered.

Hosted rendering requires a public program key, a positive TTL that is an exact multiple of 86400 seconds, and the hosted script's fixed lk_click / lk_referral cookie names. Missing or incompatible configuration renders an empty string. The days attribute is derived from linkado.tracking.ttl_seconds; there is no separate browser TTL setting. Server storage still supports second-level TTL. The hosted response may change browser cookie expiry; it never extends an existing server attribution window.

Validate the actual hosted URL and bytes against the tested contract before deployment, then test loading, cookies and CSP in the consuming application. A locally served asset or the package's Node VM contract suite alone does not prove production browser integration.

The same tracking eligibility policy gates visitor-cookie issuance and attribution capture. It receives LinkadoFeature::Tracking with the current request, its current user, and no event. Mode and feature flags are checked before resolving the policy; policy resolution/evaluation failures disable tracking for that operation without breaking the host response. Invalid optional tracking configuration is also contained; downstream application and database failures still propagate. Tracking does not emit event eligibility diagnostics.

Keep the resolver read-only and arrange middleware so the required authentication and impersonation context is available before visitor issuance and capture. Admin and impersonation rules belong to the application. Decisions and request/user context are evaluated on each operation, not cached by the package. This policy check alone does not serialize attribution with concurrent registration or identity claims.

Optional host identity locking

Applications with an anonymous identity lifecycle can bind LocksLinkadoAttributionIdentity. Without this binding, attribution remains visitor-only. The additive AttributionIdentity value object has string $key and bool $captureAllowed; the key must be a stable, opaque server-side identifier without PII. Only its namespaced SHA-256 hash is stored in identity_hash.

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Linkado\Laravel\Contracts\LocksLinkadoAttributionIdentity;
use Linkado\Laravel\Support\Attribution\AttributionIdentity;

final class HostAttributionIdentity implements LocksLinkadoAttributionIdentity
{
    public function withLockedIdentity(Request $request, Closure $operation): void
    {
        // A trusted host middleware has already resolved this opaque key.
        // Do not use an untrusted cookie, query parameter, or cached lifecycle state.
        $key = $request->attributes->get('resolved_anonymous_key');
        if (! is_string($key)) {
            return;
        }

        $identity = DB::connection(config('linkado.connection'))
            ->table('anonymous_identities')->where('key', $key)
            ->lockForUpdate()->first();

        if ($identity !== null) {
            $operation(new AttributionIdentity($identity->key, ! $identity->claimed));
        }
    }
}

$this->app->bind(LocksLinkadoAttributionIdentity::class, HostAttributionIdentity::class);

The table and lifecycle belong to the application; the package does not create them. The adapter runs inside an active transaction on linkado.connection. It must re-read the host row under FOR UPDATE and invoke Closure(AttributionIdentity): void synchronously once while holding that lock, or not invoke it if identity is unresolved. It must not commit or roll back the caller's transaction, perform remote requests, or manage pending attribution. Use the same connection for the host identity and package tables. Resolve host context before the capture middleware; the separate eligibility policy remains read-only.

During registration, call Linkado::attribution()->consume($request) before claiming the host identity, inside the registration transaction on that connection. Call consume even when visitor/source cookies or pending attribution are absent and when customer events are disabled. The identity lock remains held until the host transaction ends. All competing host claim paths must follow this protocol. Lock order is host identity first, pending attribution second. A successful claim prevents later captures even after expiry, pruning, or visitor rotation; a rolled-back claim and consumption restore the previous state. No Linkado HTTP request is made on this path.

Identity bindings are checked for each operation. An unresolved or closed identity yields no capture or snapshot; it never falls back to visitor-only mode. Capture requires a request and verified server facts. Active rows cannot change identity. Identity mismatches return no attribution without scrubbing or otherwise modifying the row, even when source cookies mismatch. Legacy rows with identity_hash = null cannot be adopted by an adapter, and bound rows cannot be consumed after removing it. Pruning an expired window does not reopen a claimed host identity; existing visitors are never merged across devices.

For upgrade, pause attribution capture and related registration transitions, publish migrations and apply 2026_09_21_000003_add_identity_hash_to_linkado_pending_attributions_table.php, check linkado:health, then enable the adapter and resume. The three published create migrations remain unchanged. Do not backfill identity associations from guesses. For rollback, disable the affected integration first; removing an adapter or reverting package code does not preserve lifecycle guarantees, and the migration must not be dropped automatically.

SSO

SSO is exposed only as the named POST route linkado.sso.launch. Launch it from a CSRF-protected form; do not link to the endpoint with GET:

<form method="POST" action="{{ route('linkado.sso.launch') }}">
    @csrf
    <button type="submit">Open Linkado</button>
</form>

The default middleware is web, auth, and throttle:6,1. Before enabling SSO, bind ResolvesLinkadoSsoUser to an application adapter:

<?php

namespace App\Linkado;

use Illuminate\Contracts\Auth\Authenticatable;
use Linkado\Laravel\Contracts\ResolvesLinkadoSsoUser;
use Linkado\Laravel\Support\ResolvedSsoUser;
use Linkado\PhpSdk\Enums\SsoRedirect;

final class LinkadoSsoUserResolver implements ResolvesLinkadoSsoUser
{
    public function resolve(Authenticatable $user): ResolvedSsoUser
    {
        return new ResolvedSsoUser(
            externalUserId: (string) $user->getAuthIdentifier(),
            email: null,
            emailVerified: false,
            displayName: (string) $user->getAuthIdentifier(),
            redirectTo: SsoRedirect::AffiliatePortal,
        );
    }
}
use App\Linkado\LinkadoSsoUserResolver;
use Linkado\Laravel\Contracts\ResolvesLinkadoSsoUser;

$this->app->singleton(
    ResolvesLinkadoSsoUser::class,
    LinkadoSsoUserResolver::class,
);

Returned SSO URLs must use HTTPS with the configured hostname (case-insensitive) and effective port; omitted HTTPS port means 443. User-info, controls, backslashes and foreign authorities are rejected. SDK and resolver failures are replaced with a safe exception without an unsafe previous-exception chain. A successful one-time URL appears only in the response's Location header; the redirect body is empty.

Adapt the resolver to the host user model and supply verified email/display-name values where available. SSO performs its SDK request outside a database transaction. Failures return to linkado.sso.error_redirect with a translated validation error, and the one-time URL is never stored in the session or package tables.

Queues and scheduler

Live delivery uses the application's queue connection. If LINKADO_QUEUE=linkado, run a worker that consumes that queue:

php artisan queue:work --queue=linkado

If LINKADO_QUEUE is unset, the job uses the connection's default queue. Horizon is optional; no dedicated Linkado worker implementation is required.

The service provider registers these overlap-protected schedules automatically:

  • linkado:recover every five minutes
  • linkado:prune daily

Run Laravel's scheduler in production and a worker locally when needed:

php artisan schedule:run
php artisan schedule:work

Only one scheduler command is needed on each invocation; the second form is intended for a long-running local process. Multi-node deployments should use a shared cache store so Laravel's overlap locks are shared.

Operator commands

Command Purpose
php artisan linkado:install [--force] Publish package resources without running migrations.
php artisan linkado:health [--json] Check configuration, database access, migrations, queue lag, stale claims, and terminal failures.
php artisan linkado:diagnose [--json] Report pending, stale, conflicting, exhausted, and corrupt events without changing state.
php artisan linkado:recover [--json] Queue due or stalled delivery for recovery.
php artisan linkado:retry {event} --operator= --reason= [--json] Retry one eligible failed event and record the operator audit fields.
php artisan linkado:prune [--json] Delete expired pending attribution.

Use --json for monitoring and automation. linkado:health exits non-zero when a failure check is present. Inspect with linkado:diagnose before manual retry; corrupt payloads and idempotency conflicts cannot be retried safely.

Failure handling

The package persists exact JSON bytes and their SHA-256 hash, then rehydrates the same official SDK DTO for every attempt. Network failures, HTTP 408/429/5xx responses, and valid Retry-After values use bounded retries. Stable 4xx responses, HTTP 409 payload conflicts, attempt exhaustion, retry-window expiry, corruption, and disabling the package terminate delivery deterministically.

Automatic delivery is limited by linkado.delivery.max_attempts and linkado.delivery.retry_window_seconds. Recovery handles dispatch failures, stale claims, and lost queued jobs after the queue uniqueness lease expires (the configured claim timeout). A late worker cannot overwrite a newer claim. Switching from live to off or shadow prevents queued event delivery. Missing live credentials are checked before claim, leaving pending snapshots and attempt counts unchanged for recovery after configuration repair. A manual retry requires both --operator and --reason, never alters the original event ID or payload, and leaves an audit attempt. It authorizes one delivery even after automatic limits have expired; an abandoned manual attempt does not authorize unlimited recovery attempts.

Listen to package lifecycle events for application-specific observability. Their context is intentionally sanitized; do not attach raw DTO payloads, cookies, credentials, or SSO URLs in listeners.

Privacy and data handling

  • Pending attribution stores a visitor hash, an optional opaque identity hash, click/referral value, and timestamps. It stores no IP address, user agent, email, or host user foreign key.
  • Outbox rows contain the official SDK payload. Send only identifiers and metadata allowed by the SDK; never add PII or secrets.
  • Tracking cookies are package-scoped. Invalid or tampered visitor cookies are rotated without logging the raw value.
  • Credentials are resolved lazily and excluded from configuration exceptions and connector debug output.
  • SSO URLs contain one-time secrets and must not be logged, persisted, or added to analytics.

Review retention requirements for the host application and keep the daily attribution prune schedule active.

Upgrading

The package follows Semantic Versioning. Within a major version, additive configuration and migration changes may require publishing new resources; breaking public API or default behavior changes require a new major version.

Version v3.0.0 makes verified visits the only strategy. It removes tracking.last_valid_visit, unverified slug/query capture, first-touch replacement rules and requestless internal capture. Applications must prepare attribution before their registration transaction and retain consume-before-identity-claim plus immutable customer outbox recording inside it. The facade, identity contract and event DTOs remain unchanged. Preserve all five migrations and existing history. No PHP SDK release or new schema migration is required when upgrading from v2.1.

Published v2.1.0 remains unchanged: consumers staying on that version select verified visits with its existing configuration key. V3 is an explicit major upgrade: install linkado-ru/laravel:^3.0 from the published release and remove the obsolete strategy key. Verify the installed source/dist reference; source checkouts alone do not prove Packagist delivery. See compatibility and release procedure.

Version v2.0.0 was a major release: live defaults and enabled event families require an explicit major upgrade from ^1.x. The supported facade/contract/getter signatures remain compatible and identity locking is opt-in. See the explicit API and schema diff and the v1-to-v2 migration below.

Consumers upgrading from v1.0 must apply the additive identity migration even without an identity adapter; v1.1 already includes it. The historical v1.1-to-v2.0 upgrade added no fifth migration; v2.1 adds the visit-order migration above. Pause affected capture and registration transitions before changing package code, verify all migrations required by the target version are applied, then check health in shadow mode with features disabled before resuming. off health deliberately marks database checks not applicable and is not migration proof. Do not guess or backfill legacy identity associations. Disable the affected integration before rollback; do not automatically remove the column or promise the old code's lifecycle safety.

Before upgrading:

  1. Read CHANGELOG.md.
  2. After release verification, change the package constraint to ^2.0 and run composer update linkado-ru/laravel --with-dependencies in a branch. The v2.1 target requires PHP SDK ^1.1.
  3. Compare the published config/linkado.php with the package default instead of overwriting local values blindly.
  4. Publish any new migrations with php artisan vendor:publish --tag=linkado-migrations and run the application's normal migration process.
  5. Run the application test suite and php artisan linkado:health --json in shadow mode before resuming capture/registration and enabling live mode.

Development

Required CI covers Ubuntu, PHP 8.3/8.4/8.5 with lowest/stable dependencies, and MySQL 8.4, MariaDB 11.4 and PostgreSQL 17. CI runs on Ubuntu only; Windows is not part of the matrix.

See the contribution guide for local validation and pull-request requirements. Security reports follow the security policy.

Maintainers use the repository's development/release AI skills and release procedure. Consuming applications use the bundled linkado-development Boost skill; after package upgrades, refresh installed skills with the application's Boost update workflow. Package checks, remote CI, published-version verification and hosted/application acceptance are separate evidence.

Linkado Laravel is open-source software licensed under the MIT license.

Historical migration: v1 to v2.0

This is a major migration: use ^2.0 only after release verification. Existing ^1.x consumers remain on their previous defaults. Before upgrade, preserve prior behavior through explicit mode/feature settings; manually merge published configuration without vendor:publish --force. Add raw url, retain raw legacy base_url, and remove old env overrides only when ready for the new defaults. For v2.0, SDK v1.0.0 and the four published migrations remained unchanged. A current v2.1 target additionally requires SDK ^1.1 and the forward visit-order migration described above. Source keys and existing outbox snapshots remain unchanged.

Automated tests must force mode off, blank URL/credentials and false feature flags through both PHPUnit environment readers, use an independent APP_CONFIG_CACHE, and explicitly enable synthetic live credentials with HTTP fakes/stray-request prevention. Hosted acceptance is separate and opt-in; pass public program/origin/referral/window settings, never a real token to browser processes.

After verified release and deployment, rebuild configuration cache and restart long-running workers through the application's normal deployment workflow. Required package CI includes the complete Ubuntu PHP/dependency/database matrix. Hosted/financial acceptance and production DNS/TLS/asset/API/SSO/CORS/program/worker readiness remain release gates.