teckwei1993/laravel-otp
A Laravel package for generating, sending, and verifying one-time passwords (OTPs). It supports configurable code length, expiry, resend limits, and custom delivery channels, making it easy to add secure OTP verification to login, registration, or sensitive actions.
This package is at v1.0.14, and the only change in this release is Laravel 13 support. That tells you two useful things before you write any code: the package is actively maintained, and it is still on a 1.x line, so no breaking API changes have shipped within this series. Treat this as a compatibility bump, not a feature release — your upgrade risk is in the framework jump, not in the package.
Minimal path to a working install:
Confirm the constraint actually resolves. Run composer require <vendor>/<package> and then check what you got:
composer show <vendor>/<package>
composer why-not <vendor>/<package> 13.x
If you are on Laravel 12 and staying there, verify the package still declares your version in illuminate/support — widening a constraint to include 13 occasionally drops older floors.
Look at these files first, in this order:
composer.json — the illuminate/* constraints and the PHP floor. Laravel 13 raises the PHP minimum; if your app or CI image is below it, the package isn't the problem, your runtime is.config/<package>.php — the knobs you'll actually tune.tests/ — for a package this size, the test suite is the most reliable usage documentation you'll get.Publish and cache-clear if the package ships config, migrations, or views:
php artisan vendor:publish --provider="Vendor\\Package\\PackageServiceProvider"
php artisan config:clear && php artisan optimize:clear
First use case: prove the wiring end-to-end in isolation. Resolve the package's main class out of the container in a single artisan command or route closure and hit it once. Do this before you thread it into domain services — it separates "package is broken" from "my integration is broken" while the surface area is still one line.
Resolve from the container, never new. The service provider exists so you can swap implementations in tests. Inject the contract or resolve via the facade; constructing directly bypasses bindings and singleton state.
public function __construct(private PackageManager $manager) {}
Publish config, then override via env. Copy the package config into config/ and edit that copy. Never edit inside vendor/. Keep the package's own defaults visible in the published file so the next developer can see what changed.
Bind a fake in the test suite. Most packages of this shape register a manager or driver interface in the container. In a test service provider:
$this->app->instance(PackageManager::class, new FakeManager());
This is the single highest-leverage integration point — it lets the package's outbound calls (HTTP, queue, storage) stay out of your feature tests.
Wrap the package behind one of your own classes. Even a thin adapter pays off here. When the package adds a method or you swap it out, you change one file instead of grepping call sites. Given the package is pre-2.0, this also hedges against a future major.
Pin the CI matrix deliberately. Since v1.0.14 explicitly adds Laravel 13, run your test suite against both your current Laravel version and 13 in CI during the transition. Testing only the newest is how a constraint widening silently breaks the version you're still deploying.
Watch queue and event boundaries. If the package dispatches jobs or events, configure a non-sync queue connection in at least one test so serialization problems surface in CI rather than production.
config:cache staleness is the #1 false alarm. After publishing config or bumping the package, run php artisan config:clear. Cached config masking a new key produces confusing "option ignored" behaviour.bootstrap/cache/packages.php and bootstrap/cache/services.php.php constraint. A failing composer update with an opaque error is usually this, not the package.composer update, you'll get divergent resolved versions of the package across the team. Commit composer.lock and treat package bumps as their own PR.dd() — it usually shows the resolved driver and config the package actually sees, which is frequently not what you think it is.1.x package over a small gap creates permanent maintenance debt.1.x, patch and minor releases should not break your integration. v1.0.14 is a patch — safe to take. Keep the constraint permissive (^1.0) rather than pinning an exact patch, so you receive these compatibility updates automatically.composer update <vendor>/<package> --with-dependencies after the upgrade, not a bare composer update, so transitive illuminate/* packages move in lockstep with the framework version you're targeting.How can I help you explore Laravel packages today?