inovanti-bank/messaging
A Laravel/PHP package from Inovanti Bank for adding messaging capabilities to your application. It offers a structured way to send, receive, and manage messages, making it easier to integrate bank-related communication workflows with minimal setup and clean, maintainable code.
Install and wire it up. The package is a Laravel service provider, so the usual flow applies:
composer require inovanti-bank/messaging
php artisan vendor:publish --provider="InovantiBank\Messaging\MessagingServiceProvider"
php artisan migrate
Package discovery should register the provider and any facade alias automatically. If it doesn't, check composer.json for extra.laravel.providers before manually adding it to config/app.php — a missing discovery block is a common early tripwire in small packages.
Set credentials before you touch code. Expect a config/messaging.php with a default channel/driver plus per-channel blocks (SMS, email, push, instant message). Copy the env keys it references into .env — typically per-driver credentials (SMS gateway SID/token, mail transport, push service key). Do this first, because most failures in week one are "no driver configured" rather than logic bugs.
Where to look, in order:
config/messaging.php — the source of truth for channels, drivers, and the queue connection.src/MessagingManager.php or similar) — this is the public API surface; read its methods before writing any call sites.src/Channels/ and src/Drivers/ — tells you exactly which transports are actually supported versus aspirational in the README.database/migrations/ — whether message/status records are persisted for you, and what columns you can query for delivery reporting.register()/boot() — extension points, bindings, and any registered routes (especially webhook endpoints).First use case: transactional, low-volume notification — an OTP by SMS or an order-confirmation email — sent through the package's single manager entry point rather than by calling a vendor SDK directly. The whole value proposition is that you write one call site and swap the driver later, so validate that promise on day one with a trivial send:
use InovantiBank\Messaging\Facades\Messaging;
Messaging::channel('sms')->send($recipient, $message);
Confirm what the actual method signature is from the manager class — see Gotchas below on why you shouldn't trust README examples here.
Route everything through one internal service. Don't call the facade from controllers. Wrap it in a thin application-level service (App\Support\MessageDispatcher or a domain-specific notifier) so that driver quirks, retries, logging, and recipient normalization live in one place. When the package's API shifts — likely, given the maturity level — you change one file instead of forty.
Wrap sends in queued jobs, not in the request cycle. Outbound messaging is I/O against a third party; a 3-second gateway timeout should not become a 3-second page load. Give each job a dedicated queue with a low retry count and an explicit $tries/backoff, and pass a stable idempotency key (order ID, notification UUID) so a retry doesn't double-send.
final class SendOrderShippedSms implements ShouldQueue
{
public int $tries = 3;
public array $backoff = [10, 60, 300];
public function handle(MessageDispatcher $dispatcher): void
{
$dispatcher->sms($this->order->phone, 'order_shipped', ['order' => $this->order->id]);
}
}
Use templates at the package layer, not string concatenation in PHP. If the package supports named templates, put copy there so marketing can change wording without a deploy. If it doesn't, keep the template key in your call and render text in your own presenter — but keep the key in the payload regardless, so your stored message records are meaningful in reports.
Normalize recipients once, at the boundary. Convert phone numbers to E.164 and validate email before they reach the dispatcher. Driver-side rejection messages for malformed numbers are usually opaque, and this is the single biggest source of "the package is broken" reports.
Channel fallback as an explicit policy. Model it in your dispatcher: try push, fall back to SMS if the push token is missing or the send fails with a transient error. Don't bury fallback logic in the package's config unless its semantics are documented — behavior on failure is exactly the kind of thing that is under-specified in a young package.
Pair with Laravel Notifications for user-facing messaging. Notification::send() gives you per-user channel preferences and via() routing for free; let the package be the transport underneath rather than re-implementing preference logic. Write a custom notification channel class that delegates to the package.
Handle delivery receipts deliberately. Most multi-channel packages register a webhook route. Exclude it from CSRF (bootstrap/app.php or VerifyCsrfToken), verify the provider signature per driver, and persist status transitions (queued → sent → delivered → failed) idempotently — webhooks retry and arrive out of order. Update by provider message ID, never append blindly.
Test with a fake. If the package ships a fake/Messaging::fake() helper, use it in feature tests and assert on dispatched messages. If it doesn't, bind a recording double to the manager's interface in the container and assert against that. Either way, no test should hit a real gateway.
Observe sends as events. Listen to whatever the package dispatches on send/failure and pipe it into your logs or metrics with the message ID, channel, and latency. Without this you cannot answer "did the customer get the OTP?" during an incident.
Calibrate expectations to the maturity level. One star, zero dependents, and a future-dated last release mean the README is the least reliable artifact in the repo. Treat the source and the service provider as authoritative; treat any documented method name, config key, or event as a hypothesis to verify in tinker. Pin the version in composer.json and read the changelog before every bump — there is no ecosystem of blog posts or Stack Overflow answers to fall back on.
Duplicate sends are the classic failure. Queued jobs with retries plus a non-idempotent downstream gateway equals customers receiving the same SMS three times. Always pass an idempotency key if the package supports one; if it doesn't, guard in your dispatcher with a cache lock keyed on the logical event ("sms:order-shipped:{$orderId}").
config:cache breaks dynamic config. Reading per-tenant credentials from the database and stuffing them into config() at runtime will silently fail in production once config is cached. Use per-send credentials passed through the call, or a driver instance resolved per tenant, not config() mutation.
"Sent" is not "delivered." A successful API call from the package usually means "accepted by the gateway." Delivery status arrives later via webhook or poll. Never show users a "Delivered" badge based on the send return value, and don't delete your own record of the message after a successful send.
Rate limits and throughput. Gateways throttle per-second and per-day. If the package doesn't expose a rate limiter, wrap sends in RateLimiter::attempt() or a Redis-backed limiter. Bulk campaigns should be chunked with Bus::batch() rather than looped, and given a separate queue so a marketing blast can't starve OTPs.
Driver payloads are not interchangeable. The unified channel abstraction will have a lowest common denominator, and driver-specific options (sender ID, DLT template IDs, push TTL/collapse keys, rich media) will need to be passed through a raw/extras array — or won't be supported at all. Check the driver source before promising a feature to product.
Timeouts and silent failures. Confirm the HTTP client timeout in the driver; Laravel's default can be far longer than your queue's retry_after, which causes jobs to run twice. Log every non-2xx response with the provider body, and distinguish transient (5xx, 429) from permanent (4xx, invalid recipient) failures so you don't retry the unretryable.
Webhook endpoint exposure. Package-registered routes may be unauthenticated by default. Verify signatures, restrict by IP if the provider publishes ranges, and make the handler idempotent and fast — queue the processing, acknowledge with a 200 immediately.
Extension points worth knowing: a driver contract/interface you can implement for an in-house gateway, container bindings in the service provider you can override, and published config. If no driver interface exists, that's your signal to keep the package at arm's length behind your own dispatcher interface rather than leaking it into domain code — and it's a strong argument for evaluating a more established alternative before this becomes load-bearing.
How can I help you explore Laravel packages today?