Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tinkoff Auth Laravel Package

cashier-provider/tinkoff-auth

A Laravel package that adds Tinkoff authentication support to Cashier-style payment integrations, helping manage merchant credentials and secure API access for Tinkoff acquiring flows.

View on GitHub
Deep Wiki
Context7

Getting Started

This package wires T-Bank (Tinkoff) acquiring into Laravel's Cashier conventions — authentication for the merchant API plus the payment lifecycle (init / authorize / confirm / cancel / refund). Start here:

  1. Install and publish config

    composer require cashier-provider/tinkoff-auth
    php artisan vendor:publish --tag="tinkoff-auth-config"
    php artisan migrate
    
  2. Credentials first, everything else second. Populate .env before touching any code — TINKOFF_TERMINAL_KEY, TINKOFF_PASSWORD, and the notification/webhook secret are what 90% of "it doesn't work" issues boil down to. Note that Tinkoff's Password is a terminal password, not your account password, and it differs between test and production terminals.

  3. Read the published config file as your map. It is the most reliable documentation surface in this package: it names the API base URL, the notification route, the model bindings, and the HTTP client/timeout options. Cross-reference it against the README before reading any source.

  4. Run one sandbox payment end-to-end before writing feature code. Use the test terminal, hit init, follow the PaymentURL, pay with a test card, and confirm you receive a webhook at your /tinkoff/notify route. This single loop validates credentials, URL reachability (webhooks cannot hit localhost — use a tunnel), token signing, and event dispatch at once.

  5. First real use case: a single-product checkout where you init a payment, redirect the user, and mark an order paid in the notification handler. Do not start with subscriptions or partial refunds; the package's value is clearest on the straight-through path.

The 2.6.1 release itself carries a changelog link only — no migration steps or breaking notes. Treat it as a routine patch: bump, run the suite, and confirm the notification handler still verifies tokens.

Implementation Patterns

Two-phase (auth/confirm) is the reason to use this package. If you are doing pre-authorization holds — hotels, rentals, delayed fulfilment — use the authorize-then-confirm flow rather than a single init. Authorize at order time to validate the card, confirm when you ship, cancel on timeout. Model the hold as a local payments row with a status enum, because the remote state and local state will drift and you need a reconciliation point.

Never trust the redirect back to your site. The SuccessURL/FailURL return is a UX affordance only. Grant entitlements exclusively from the server-to-server notification, and make the notification handler idempotent — keyed on the remote PaymentId. Duplicate notifications are normal, not exceptional:

return DB::transaction(function () use ($request) {
    $payment = Payment::where('remote_id', $request->PaymentId)->lockForUpdate()->firstOrFail();

    if ($payment->isSettled()) {
        return 'OK'; // already processed, acknowledge again
    }

    $payment->markSettled($request->Status);
    OrderFulfilled::dispatch($payment->order);

    return 'OK';
});

Wire payment events to your domain, not to the package. Map the package's events onto your own (OrderPaid, PaymentFailed, PaymentRefunded) so your application never imports vendor namespaces outside the payment service layer. This is the single highest-leverage habit for later swapping providers.

Wrap the client behind your own service. A thin PaymentGateway interface with charge(), authorize(), capture(), refund() implemented by an adapter over this package keeps controllers thin and makes the package swappable. Inject the package client, don't app() it from a controller.

Queue everything that talks to the API except the initial init. Confirmations, captures, and refunds can run in jobs with retries and backoff. The init must stay synchronous because you need the PaymentURL to render in that request.

Local development: tunnel webhooks (valet share, ngrok, or Expose) and point the notification URL in the merchant cabinet at the tunnel. A common pattern is a TINKOFF_NOTIFY_URL env override so sandbox and prod terminals can share code while targeting different hosts.

Testing: fake the package's HTTP layer with Http::fake() and assert against recorded fixture payloads for Init, Confirm, Cancel, GetState. Do not mock your own service in feature tests — mock at the HTTP boundary so token-signing and response-mapping code is actually exercised.

Gotchas and Tips

Token signing is order-sensitive. Tinkoff's token is a SHA-256 over a concatenation of selected fields in a prescribed order plus the terminal password. Adding a field to the payload without adding it to the token inputs (or vice versa) produces a 204 / "invalid token" error with no field-level explanation. When debugging an auth failure, dump the exact array being signed and diff it against the API docs field list. Nested objects and arrays are excluded from signing — that trips people up on Receipt and DATA.

Amounts are in kopecks. Amount: 100000 is 1,000.00 ₽. Cast consistently at the boundary — a Money value object or a single toKopeks() helper — and never let floats reach the client.

The notification endpoint must return the literal string OK with a 200. Returning JSON, a redirect, or true will cause Tinkoff to retry the notification repeatedly, and your idempotency logic then becomes load-bearing rather than defensive.

Verify the notification token too. The inbound webhook is signed with the same scheme. Skipping verification means anyone who learns your URL can mark orders paid. Verify before you touch the database, and return OK on verification failure only if you want to suppress retries — usually you don't.

Idempotency keys on init. Send your own stable OrderId (or an explicit idempotency key if supported by your terminal) so a retried request doesn't create a second payment. Network timeouts are ambiguous: the payment may exist. On timeout, GetState by OrderId before re-initializing.

Status is a string enum, and there are more states than you expect. NEW, FORM_SHOWED, AUTHORIZING, AUTHORIZED, CONFIRMED, REJECTED, CANCELED, REFUNDED, PARTIAL_REFUNDED, DEADLINE_EXPIRED. Handle the terminal states exhaustively with a match that throws on unknown values rather than falling through to "failed" — a new status shouldn't silently cancel a customer's order.

Config quirks. Config is cached in production; php artisan config:clear after changing credentials is mandatory. If the package reads credentials at container-resolution time rather than per-request, long-running Octane workers will hold stale values across deploys.

Extension points to keep in reach: the notification controller (extend or override the route if you need middleware or per-terminal routing), the payment model binding (swap in your own model via config to add columns), and the HTTP client (replace to add logging, correlation IDs, or a custom Handler for Guzzle retries). Prefer these over forking.

Release hygiene. This provider's releases are frequently changelog-link-only, so patch numbers alone don't tell you whether signing or payload shapes moved. Pin to a caret constraint, keep a sandbox integration test in CI that runs a real init against the test terminal, and read the commit list before bumping minor versions. That test is your real upgrade gate — not the version number.

Log aggressively but redact. Log the request payload minus Token, Password, and card data, plus the response PaymentId and Status. When support asks why a payment failed, that trail is worth more than any debugger session.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource