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

Laravel Sens Laravel Package

seungmun/laravel-sens

Integrate NHN Cloud SENS with Laravel to send SMS, LMS, MMS, and KakaoTalk AlimTalk/FriendTalk messages. Configure credentials and sender numbers, then send directly or via Laravel notifications with a clean, simple API. Ideal for reliable transactional and promotional messaging.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish config

composer require seungmun/laravel-sens
php artisan vendor:publish --tag=laravel-sens-config

The package ships a config/sens.php that reads NCloud SENS credentials. Populate your .env:

NCLOUD_SENS_SERVICE_ID=your-service-id
NCLOUD_SENS_ACCESS_KEY=...
NCLOUD_SENS_SECRET_KEY=...
NCLOUD_SENS_FROM=01012345678

The FROM number must be a sender number registered and approved in the NCloud SENS console for that service ID — an unregistered sender will fail at the API, not in your app.

Where to look first

  • config/sens.php — the only config surface; confirm the env keys it maps to, since older publishes can drift.
  • src/Channels/ — the notification channel classes (Sms/Lms/Mms or a combined channel depending on release). This is what you put in via().
  • src/SensMessage.php — the fluent message builder your toSens() method must return.

First use case: a phone-verification notification

// app/Notifications/SendVerificationCode.php
use Illuminate\Notifications\Notification;
use Seungmun\LaravelSens\Channels\SensSmsChannel;
use Seungmun\LaravelSens\SensMessage;

class SendVerificationCode extends Notification
{
    public function __construct(public string $code) {}

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

    public function toSens($notifiable): SensMessage
    {
        return SensMessage::create("[MyApp] verification code: {$this->code}")
            ->from(config('sens.from'))
            ->to($notifiable->phone);
    }
}

Then $user->notify(new SendVerificationCode('123456'));. This is the bread-and-butter flow: short transactional SMS triggered from application events.

Implementation Patterns

Notifiable routing. The idiomatic Laravel way is to keep the destination phone number on the model and expose it to the channel:

public function routeNotificationForSens($notification): ?string
{
    return $this->phone; // normalized, digits only
}

If you add this, toSens() does not need to call ->to() at all — the channel resolves the recipient. Prefer this over hardcoding numbers per notification, so you can swap the model field without touching message classes.

Length-driven SMS vs LMS. SENS picks the transport by payload size, not by the class you pick in via(). Keep transactional messages under ~90 bytes (Korean characters count roughly 2 bytes in EUC-KR) so a "SMS" stays an SMS; anything longer is billed and delivered as LMS. If you genuinely need both paths, branch explicitly:

public function toSens($notifiable)
{
    $body = $this->buildBody();
    $message = SensMessage::create($body)->from(config('sens.from'));

    return strlen(mb_convert_encoding($body, 'EUC-KR')) > 90
        ? $message->asLms()   // if the channel exposes it
        : $message;
}

Fan-out with other channels. Combine SENS with mail or database in the same via() array. Because Laravel dispatches channels sequentially, order SENS after cheaper channels so a mail failure short-circuits before you spend SMS credits:

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

Queue it. SMS are slow, synchronous HTTP calls. Implement ShouldQueue on notifications that go to SENS, and set a short tries/backoff on the notification so retries don't burn credits repeatedly for a bad number. If you need per-recipient throttling, use RateLimited or a middleware-backed queue job.

Template-based sends. NCloud SENS supports pre-registered templates keyed by template ID. If your org requires template approval (common for marketing/alimtalk-adjacent flows), drive messages through the template ID rather than free-form text, and keep the template variables on the notification object so marketing can update copy without a deploy.

Service layer wrapping. Don't scatter SensMessage construction across dozens of notifications. Centralize with a small helper:

final class Sms
{
    public static function make(string $body, ?string $to = null): SensMessage
    {
        $message = SensMessage::create($body)->from(config('sens.from'));
        return $to ? $message->to($to) : $message;
    }
}

This gives you one place to add logging, sanitization, or a "dry run" toggle in local/testing.

Testing. In feature tests, use Notification::fake() and assert Notification::assertSentTo($user, SendVerificationCode::class) — this avoids hitting SENS entirely. For unit tests of the message builder, construct SensMessage directly and inspect from/to/content without an HTTP call.

Gotchas and Tips

Last release was 2022 — verify the API surface. NCloud SENS changed its endpoint and signature scheme (per-service IDs, x-ncp-apigw-* headers, HMAC-SHA256 over method+path+timestamp) over time. Before shipping, send one real message from a staging environment and check the package's src/ against the current NCloud SENS REST docs. If you hit 401/403s, it's almost always a stale signature implementation or a wrong SERVICE_ID — not your credentials.

Config caching will mask .env edits. After changing NCLOUD_SENS_* values, run php artisan config:clear. On Forge/Envoyer/Vapor, remember that the value compiled into the config cache is what actually gets used at runtime.

Phone number format. Normalize to digits only (01012345678) before handing to the channel. Hyphenated numbers, +82 prefixes, and leading zeros stripped by Excel imports are the most common cause of "successful API call, no delivery." Also validate against a Korean mobile regex server-side — SENS will happily accept an invalid number and just not deliver.

Timestamps and clock skew. The signed request includes a timestamp; containers with drifting clocks will intermittently 401. If you see sporadic auth failures that resolve on retry, apt install chrony (or your platform's NTP) is the fix, not a code change.

Delivery ≠ acceptance. An HTTP 202 from SENS means "accepted for delivery," not "delivered." If you log success from the API response, you will overstate delivery. For anything where delivery matters (OTP audit, billing), poll the SENS message status endpoint or webhook the result back into your app; the package typically only wraps the send call.

Silent failures in try/catch. The channel will throw on non-2xx responses. Wrap queue-dispatched notifications so a failure is logged with the recipient and body hash (never the plaintext code) and does not rethrow into an infinite retry loop. Set public $tries = 1 on OTP notifications to prevent duplicate codes.

Length limits and encoding. SensMessage likely measures bytes, not characters. Emojis and Korean text push you from SMS to LMS silently and change cost. If cost control matters, assert length in a test:

$this->assertLessThanOrEqual(90, strlen(mb_convert_encoding($body, 'EUC-KR')));

Extension points. The channel resolves credentials from config('sens.*') at send time, so a multi-tenant setup can override at runtime with config(['sens.service_id' => $tenant->sensServiceId]) inside a queued job — but do this per job, never globally, or you'll leak one tenant's sender number into another's notification.

Local development. There is no official SENS sandbox. Add an early-return guard in your Sms::make() helper when app()->environment('local'), or notifications will burn real credits (and real SMS to real people) during local feature tests.

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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk