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

Smsir Php Laravel Package

amirbagh75/smsir-php

A PHP/Laravel package for integrating the SMS.ir API, making it easy to send SMS messages, use verification codes, and manage SMS-related features from your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish config

composer require amirbagh75/smsir-php
php artisan vendor:publish --provider="Amirbagh75\SmsirPhp\SmsirServiceProvider"

The package follows the standard Laravel package shape, so auto-discovery should register the service provider and a Smsir facade. If discovery fails (or you're on an older Laravel), add the provider and alias to config/app.php manually.

Where to look first

  1. config/smsir.php — the only file you actually need to touch. It will carry an api_key, a default line_number, and likely a timeout/base-URI entry. Map these to .env:
SMSIR_API_KEY=your-panel-api-key
SMSIR_LINE_NUMBER=3000xxxx
  1. src/ — read the client class before writing anything on top of it. This package is at 17 stars with a last release in February 2023; the exact method surface (sendMessage, sendVerifyCode, sendBulk, getCredit, getLineNumbers, or variations) is what you must confirm rather than assume. Compare what you find against the current sms.ir REST docs — the vendor has moved endpoints between v1 and v2, and a two-year-old wrapper may target the older path.

  2. The facade accessor string. Confirm it in the provider's register() before you type Smsir:: in application code.

First use case: OTP delivery

For an Iranian-facing app this is almost certainly why you're here. The pattern is template-based (sendVerifyCode-style), not free-text — the panel holds a pre-approved template with placeholders, and you pass a template ID plus a parameter array:

use Amirbagh75\SmsirPhp\Facades\Smsir;

Smsir::sendVerifyCode($mobile, $templateId, [
    'Code' => $code,
]);

Get that one path working end-to-end against a staging mobile number before building anything else. Free-text promo sending is a separate, later concern.

Implementation Patterns

Wrap the facade — don't call it from controllers

Given the maintenance profile, treat amirbagh75/smsir-php as an implementation detail behind your own contract from day one. This is the single highest-leverage decision in this assessment.

interface SmsGateway
{
    public function sendOtp(string $mobile, string $code): void;
    public function sendText(string $mobile, string $message): void;
    public function balance(): int;
}

final class SmsIrGateway implements SmsGateway
{
    public function __construct(private readonly int $otpTemplateId) {}

    public function sendOtp(string $mobile, string $code): void
    {
        Smsir::sendVerifyCode($this->normalize($mobile), $this->otpTemplateId, [
            'Code' => $code,
        ]);
    }
}

Bind it in a service provider. Swapping providers later becomes a one-line rebind instead of a grep across the codebase.

Use it as a Laravel notification channel

If the package exposes a notification channel class, register it and use toSmsir() on notifications so OTP and transactional messages share the queue, retry, and logging infrastructure:

public function via($notifiable): array
{
    return [SmsirChannel::class];
}

public function toSmsir($notifiable): SmsirMessage
{
    return SmsirMessage::create()
        ->template($this->templateId)
        ->parameters(['Code' => $this->code]);
}

If no channel ships with the package, write a 30-line one that delegates to your SmsGateway — that's cheaper than coupling your notification classes to the facade.

Always queue OTP and bulk sends

The sms.ir API is an external HTTP call with real latency; blocking a login request on it is a self-inflicted outage. Put sends behind ShouldQueue:

final class SendOtp implements ShouldQueue
{
    use InteractsWithQueue, Queueable;

    public int $tries = 3;
    public int $backoff = 5;

    public function handle(SmsGateway $gateway): void
    {
        $gateway->sendOtp($this->mobile, $this->code);
    }
}

Note the tradeoff: queueing OTP introduces a delivery delay. If your product needs sub-second code delivery, send synchronously but wrap in an explicit timeout and have a fallback (voice call, resend button with cooldown).

Bulk sends: chunk and throttle

There is almost certainly a per-request recipient cap and a per-second rate limit on the panel. Chunk with array_chunk($numbers, 100), and throttle the job:

foreach (array_chunk($numbers, 100) as $batch) {
    Smsir::sendBulk($batch, $message);
    usleep(200_000);
}

Scheduled credit monitoring

getCredit() is cheap and worth a daily scheduled command that alerts your team when balance drops below a threshold. Silent credit exhaustion is the classic way an SMS-dependent app breaks in production — the sends start failing and nobody notices until support tickets arrive.

$credits = app(SmsGateway::class)->balance();
if ($credits < config('sms.low_balance_threshold')) {
    // Slack / email the on-call dev
}

Normalize phone numbers at the boundary

Do this once, in the gateway, not at every call site: strip spaces and dashes, accept +989…, 00989…, 989…, and 09…, and always emit 09xxxxxxxxx. The API is strict, and a single non-normalized path produces the most confusing class of bug you'll hit with this package.

Test with a fake gateway, not with HTTP fakes

Because the package's internal HTTP client is not your code, don't try to Http::fake() its calls. Bind a FakeSmsGateway in tests that records messages:

$this->app->instance(SmsGateway::class, $fake = new FakeSmsGateway());
// ...
$fake->assertSentTo('09121234567');

Gotchas and Tips

The package is two years stale — audit the endpoints before trusting them

Last release 2023-02-05. sms.ir has revised its API since; verify each call you depend on against current vendor documentation and be prepared to bypass the wrapper for any endpoint that has moved. Pin the version in composer.json ("amirbagh75/smsir-php": "1.*") and do not run composer update on it casually in a working system.

Template parameters are exact-match and unforgiving

The parameter keys ('Code', 'Name', …) must match the template placeholders defined in the sms.ir panel character-for-character, including case. Mismatches typically do not throw a helpful exception — you get a failed send or a delivered message with literal blank spaces where the value should be. Keep template IDs in config, not hardcoded, and sanity-check every new template with a real phone.

Persian text changes your billing math

Non-ASCII (Persian) characters are billed at roughly 70 characters per SMS segment versus ~160 for Latin. A "short" Persian message can silently become two or three segments. If you're estimating cost or building a character counter in an admin UI, use the Persian limit.

Never log the API key or full recipient numbers

Iranian data-protection expectations (and general good hygiene) mean OTP codes and full mobile numbers should not land in your logs or in queued-job payloads that get serialized to the queue driver's storage. Hash or mask numbers in logs; keep the code out of exception messages.

Config caching will bite you

After publishing, php artisan config:cache in deploy is mandatory — but remember that any runtime config(['smsir...' => ...]) mutation stops working once cached. Read config in constructors, not at call time.

Check the HTTP client for timeout and exception behavior

Open the client class and find out: does it set a timeout? Does it throw on non-2xx, or return an array with an error field? A wrapper that returns ['status' => 1, ...] on error and ['status' => 0, ...] on success (a common convention in this ecosystem — note the inversion) will let failures pass silently if you only check for exceptions. Wrap calls and assert on the response explicitly.

try {
    $result = Smsir::sendVerifyCode($mobile, $templateId, $params);
} catch (\Throwable $e) {
    report($e);                       // log it
    throw new SmsDeliveryFailed(...); // and surface it
}

Delivery reports are likely pull-only

Do not assume webhook/callback support. If your product needs per-message delivery status, you will probably be polling a report endpoint on a schedule and reconciling against your own message_id. Store the vendor's returned message ID on your own model at send time — it is the only join key you'll have later.

Extension points are thin, so build them yourself

Don't expect events, macros, or a pluggable transport. The realistic extension surface is: your SmsGateway contract, your notification channel, and your own queued jobs. That's sufficient — but it means any behavior you want (retry policy, circuit breaker, per-tenant API keys) lives in your app, not the package.

Multi-tenancy caveat

If different tenants need different panel credentials, the facade's single-config model won't help. Instantiate the underlying client directly with per-tenant credentials rather than relying on the singleton Smsir:: binding.

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