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

Lhv Connect Laravel Package

swiftmade/lhv-connect

Laravel client for LHV Connect, the bank's API for corporate payments and account data. It wraps authentication, payment initiation, and statement retrieval in an expressive, configurable package, so you can integrate LHV banking into your PHP application with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

swiftmade/lhv-connect wraps the LHV Connect (LHV Pank corporate banking) API — payments, account statements, balances — behind a Laravel-friendly client. Version 0.2.0 is a framework-compatibility release (Laravel 12 & 13), so the integration surface is unchanged from 0.1.x.

Minimal setup:

  1. composer require swiftmade/lhv-connect
  2. Publish config: php artisan vendor:publish --tag=lhv-connect-config (check php artisan vendor:publish output if the tag differs in your version).
  3. Provision credentials — LHV Connect is mTLS: you need a client certificate, private key, and key passphrase issued/registered with the bank, plus your organisation's client ID.
  4. Fill .env (typical keys, confirm exact names in config/lhv-connect.php):
LHV_CONNECT_BASE_URL=https://sandbox.lhv.ee/connect/v1
LHV_CONNECT_CLIENT_ID=your-org-id
LHV_CONNECT_CERT_PATH=/var/www/app/storage/app/private/lhv/cert.pem
LHV_CONNECT_KEY_PATH=/var/www/app/storage/app/private/lhv/key.pem
LHV_CONNECT_KEY_PASSWORD=secret
  1. First use case: pull an account statement. It is read-only, idempotent, and proves your certificate chain, base URL, and client wiring all work before you risk moving money. Send a payment only after statements succeed end-to-end in sandbox.

Where to look first: config/lhv-connect.php (all knobs live here), then the package's client/service class and any src/Messages or src/Requests directory — the shape of those classes tells you whether you build typed DTOs or raw arrays. Read the package's own tests; for banking packages they are usually the best documentation of expected payloads.

Implementation Patterns

Isolate the package behind your own service. Bind a BankingService interface in a service provider and let the package be one implementation. Banks rotate endpoints, XML schema versions, and certificate requirements; you do not want LhvConnect::send() scattered across forty controllers when that happens.

$this->app->bind(PaymentGateway::class, LhvPaymentGateway::class);

Certificate storage. Keep PEMs in storage/app/private/lhv/ (never in the repo, never in public/), mount them from a secret manager in production, and give the filesystem user read-only permissions. Sandbox and production certificates are different — model them as two named connections in config rather than one mutable env var.

Payments: build, validate, then send. Construct the payment (creditor IBAN, amount as a string, currency, reference, end-to-end ID), validate locally against the bank's XSD if the package exposes that, then submit. Persist the request payload and the bank's response in your own table keyed by your internal payment ID — the bank's message ID alone is not enough to reconcile.

Statements: fetch, persist, parse. Pull camt.053 XML on a schedule, store the raw XML, then parse into transactions rows with a unique constraint on (bank_account, bank_reference, booking_date). Reprocessing is common (overlapping date windows, retries), so the unique index is your idempotency guard, not a firstOrCreate scattered in the parser.

Status polling. LHV Connect payment status is asynchronous in practice — a submitted payment is accepted, then settles later. Run a queued job on a schedule that polls pending payments older than N minutes and transitions them. Cap the polling window (e.g. 7 days) so a stuck payment does not poll forever.

Testing. Fake the HTTP layer (Http::fake() with captured sandbox XML fixtures) rather than mocking the package's classes — that keeps your tests valid across patch releases. Keep one opt-in integration test tagged @group sandbox that hits the real sandbox with a €0.01 payment.

Config in CI. Bank credentials are not available in CI. Make the connection lazily resolved so php artisan config:cache, route listing, and unit tests never touch the certificate files.

Gotchas and Tips

  • The 0.2.0 upgrade is low risk but not zero. It only widens the framework constraint. There are no breaking API changes, but a widened constraint can pull newer transitive dependencies (guzzlehttp/guzzle, XML libs) that do change behaviour. Run your sandbox test after upgrading, not just the unit suite.
  • mTLS failures are opaque. A missing intermediate certificate, wrong key passphrase, or expired cert typically surfaces as a bare 403 or a cURL handshake error with no body. Test the certificate independently with curl --cert ... --key ... before blaming the package.
  • Certificate expiry will page you at 3am. Store the notAfter date in config/DB and add a scheduled check with a 30-day warning. This is the single most common production outage for bank integrations.
  • Never log the key passphrase or full request bodies. Payment payloads contain IBANs and names (PII), and the signed request may embed credentials. Log the message ID and status, not the envelope.
  • Amounts are strings, not floats. 0.1 + 0.2 problems in a payment are unacceptable. Cast at the boundary, never before.
  • config:cache and env(). If you add your own logic, read config values via config(), not env(). Direct env() calls inside package-adjacent code return null in production and work fine locally — a classic "works on my machine".
  • Sandbox ≠ production. Sandbox frequently auto-approves payments and returns statements instantly; production has approval workflows and settlement cut-off times (TARGET2/SEPA windows). Do not encode sandbox latency assumptions into your polling intervals.
  • Duplicate submissions. Network timeouts on a payment POST leave you unsure whether it landed. Always send a stable end-to-end ID derived from your internal payment record so the bank can deduplicate, and reconcile via statement lookup before retrying.
  • Date and timezone formatting. LHV expects Europe/Tallinn business dates and ISO-8601 timestamps in the XML. Laravel's default UTC app timezone silently shifts dates and can push a payment into the wrong value date.
  • Extension points to lean on: the published config file (base URLs, timeouts, retry counts), Laravel's HTTP client factory (inject custom middleware for logging/metrics rather than editing package code), and the container binding if the package registers its client as a singleton — swap it in tests.
  • Pin the version. Pre-1.0, treat minor bumps as potentially breaking and pin "swiftmade/lhv-connect": "~0.2.0" rather than ^0.2.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin