overtrue/easy-sms
overtrue/easy-sms is a PHP SMS component with one unified API for dozens of Chinese cloud providers. Configure multiple gateways, use built-in or custom rotation and failover strategies, auto-select an available provider, and get consistent responses for logging and monitoring. Easily add or remove...
Architecture fit. overtrue/easy-sms is a framework-agnostic Composer library, not a Laravel package. There is no service provider, facade, publishable config, or migration — you write the glue. The core abstraction is small and clean: a config array builds an EasySms instance, a Message object (or an array payload) defines content / template / data, a Gateway interface normalizes each provider, and a Strategy picks gateways. PhoneNumber handles E.164 country codes, and extend() lets you register in-house gateways. For a TPM this is a favorable shape: the library owns the provider matrix and you own the application seam. The main architectural caveat is that the public contract is a nested array keyed by gateway name (gateway / status / result / exception), not typed DTOs. That is easy to log but weak for static analysis, exhaustive match handling, and long-term BC guarantees. Treat the array shape as an implementation detail behind your own SmsResult value object.
Integration feasibility. High for a Laravel team. Install is one Composer line; wiring is a singleton binding plus a config file; a Laravel Notification channel can be written in ~50 lines. A third-party channel (yl/easysms-notification-channel) exists but adds a second single-maintainer dependency — I would not take that hop for a critical path. Two concrete gates: (1) the current major requires PHP >= 8.4, so any app on 8.1/8.2/8.3 must either pin an older major or defer; (2) the README's Message examples use implicitly nullable parameters (GatewayInterface $gateway = null), which are deprecated in PHP 8.4 — expect to write ?GatewayInterface $gateway = null in your own subclasses and expect noisy deprecation output from any un-updated vendor code you copy.
Technical risk. Ranked:
AGENTS.md signals a deliberately conservative, minimal-change maintenance culture — good for stability, but it also means feature requests for your edge case will likely land locally, not upstream. The opportunity score of 22.42 reinforces this: do not plan on upstream delivering your roadmap.Key questions. Which providers do we actually need, in which countries? What PHP minor is our fleet standardized on, and when does 8.4 land? Do we need DLR/webhook callbacks and inbound (this package does not provide them)? Who owns signature/template registration and the regulatory review cycle — and how many days does that add to every content change? Is the HTTP client injectable for offline tests, or do we need HTTP-level stubbing? What is our per-message cost ceiling and daily cap?
Stack fit. PHP 8.4 + Laravel 10/11/12, Composer, queue workers. Bind EasySms as a singleton in a small service provider, source config from config/easy-sms.php reading env() for credentials — keep the config array plain and scalar so php artisan config:cache works (closures belong in the message payload, never in config). Wrap it in your own SmsSender port interface so the vendor is an adapter, not a dependency your domain code sees.
Migration path. Do not cut over the OTP path first. Sequence: (a) install and run the errorlog gateway in local/CI — zero-cost, zero-network, and it validates payload construction without provider accounts; (b) pilot one low-stakes notification (internal staff alerting) end-to-end; (c) add a second gateway and exercise failover deliberately by revoking a key in staging; (d) migrate OTP behind the port with shadow-send comparison against the incumbent; (e) decommission legacy direct-SDK calls. Budget provider-side lead time up front: in China, signature and template registration plus real-name verification is an external approval workflow measured in days, and it blocks step (b), not step (a).
Compatibility. The library itself is Laravel-version-insensitive, so the binding constraint is PHP, not the framework. Pin the major in composer.json, commit the lock file, and gate upgrades behind a smoke test that sends to a test number on each configured gateway. Skip yl/easysms-notification-channel; write the channel yourself so you control retry, backoff, and metrics, and so your Notification classes never surface gateway names.
Sequencing. Secrets management → config + singleton binding → errorlog gateway in dev/CI → first real gateway → custom Notification channel → second gateway + failover strategy → structured logging and metrics → rate limits and cost guardrails → signature/template governance runbook. Do not enable multi-gateway failover until you have per-attempt metrics; otherwise you cannot distinguish "provider down" from "our config is wrong," and you will pay three times per message to find out.
Maintenance. Low code churn, non-trivial integration churn. Provider APIs drift; the long-tail gateways in particular will break first and be fixed last. Pin versions, enable Dependabot/composer audit, and add a per-gateway contract test using recorded HTTP fixtures so an upgrade that silently changes request shape fails in CI rather than in production OTP. Accept that you will vendor-patch: fork or wrap, and keep the diff isolated behind the port. MIT license means no legal friction, but it also means no vendor to escalate to.
Support. No commercial support, Chinese-primary documentation, community issue tracker. Your on-call is the SLA. Write a runbook covering: all-gateways-failed triage, single-gateway degradation, credential rotation, provider console checks (blacklists, keyword filters, quota), and the manual "resend OTP" escape hatch for support agents.
Scaling. Stateless and HTTP-bound, so it scales horizontally without thought — the bottleneck is provider QPS and daily quotas, not the library. Enforce per-provider rate limits in the queue layer (Laravel rate limiter keyed by gateway), cap concurrency per gateway, and shard queues by provider so one slow vendor does not starve others. Normalize all numbers to E.164 before send, dedupe, and keep a short-window send ledger to suppress duplicate OTPs from user double-taps.
Failure modes to instrument explicitly. (1) NoGatewayAvailableException — the single signal that the whole SMS capability is down; alert on it, and use getResults() to emit one structured log line per gateway attempt. (2) Unknown-delivery timeouts — record the timeout as "indeterminate," never as "failed," and never blind-retry OTP sends. (3) Partial success — one gateway succeeded, others failed; the array makes this easy to mishandle, so normalize to a single terminal status at the port boundary. (4) Billing amplification — failover across N gateways multiplies cost on every degraded incident; cap fan-out at two and add a circuit breaker. (5) Silent content rejection — some providers return a success envelope with a non-success business code, and carrier keyword filtering can drop messages post-acceptance; parse provider codes, do not trust HTTP 200.
Ramp-up. Low. A mid-level Laravel developer is productive in half a day against the errorlog gateway and a real one in two days. Production-readiness — queueing, per-gateway metrics, cost guards, secrets, runbook, contract tests — is roughly one engineer-week for one developer plus a half-day of finance/legal review of per-message cost and consent/opt-in obligations (PIPL/TCPA depending on audience). The real learning curve is not the library; it is the provider onboarding, template approval, and compliance workflow that sits outside the code entirely. Staff that time accordingly and do not let it surprise the launch date.
How can I help you explore Laravel packages today?