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

Aliyun Sms Sdk Laravel Package

mrgoon/aliyun-sms-sdk

mrgoon/aliyun-sms-sdk is a lightweight PHP/Laravel wrapper for Alibaba Cloud (Aliyun) SMS. It simplifies sending verification codes and notifications through the Aliyun SMS API, with straightforward configuration and easy integration into Laravel applications via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package.

    composer require mrgoon/aliyun-sms-sdk
    

    The package is a thin Composer wrapper around Aliyun’s SMS core code (the "核心代码" from the official SDK). It ships no Laravel service provider, no config file, and no facade, so treat it as a library you wire up yourself.

  2. Read vendor/mrgoon/aliyun-sms-sdk/src/ before writing anything. The README is minimal. Look for the request class (typically SendSmsRequest), the client/profile classes, and the response object. Confirm the exact namespace (usually Mrgoon\AliyunSmsSdk\... or a mirrored Aliyun\Core\... structure) and the constructor signatures — they differ between forks of the official SDK.

  3. Prepare the Aliyun console first. You need:

    • AccessKey ID/Secret (prefer a RAM sub-user with AliyunDysmsFullAccess, not the root key).
    • An approved SignName (e.g. MyApp).
    • An approved TemplateCode (e.g. SMS_123456789) whose variable placeholders (${code}) match your payload exactly. Without approved sign + template, every call fails with isv.SMS_SIGNATURE_ILLEGAL / isv.SMS_TEMPLATE_ILLEGAL regardless of code correctness.
  4. First use case: OTP login. Put credentials in config/services.php so they are not scattered in code:

    // config/services.php
    'aliyun_sms' => [
        'access_key_id'     => env('ALIYUN_SMS_ACCESS_KEY_ID'),
        'access_key_secret' => env('ALIYUN_SMS_ACCESS_KEY_SECRET'),
        'region'            => env('ALIYUN_SMS_REGION', 'cn-hangzhou'),
        'sign_name'         => env('ALIYUN_SMS_SIGN_NAME'),
    ],
    

    Then send:

    $request = new SendSmsRequest();
    $request->setPhoneNumbers('13800138000');
    $request->setSignName(config('services.aliyun_sms.sign_name'));
    $request->setTemplateCode('SMS_123456789');
    $request->setTemplateParam(json_encode(['code' => '482913'], JSON_UNESCAPED_UNICODE));
    $response = $client->getAcsResponse($request);
    

    Success is $response->Code === 'OK'; anything else is a business error to map.

Implementation Patterns

Wrap it in an app-level interface, not raw SDK calls. This is the single most valuable pattern for a 4-star, zero-dependency package: you get a swap point when the package dies.

// app/Services/Sms/SmsSender.php
interface SmsSender
{
    public function send(string $phone, string $templateCode, array $params): SmsResult;
}
// app/Services/Sms/AliyunSmsSender.php
final class AliyunSmsSender implements SmsSender
{
    public function __construct(private DefaultAcsClient $client, private string $signName) {}

    public function send(string $phone, string $templateCode, array $params): SmsResult
    {
        $req = new SendSmsRequest();
        $req->setPhoneNumbers($phone);
        $req->setSignName($this->signName);
        $req->setTemplateCode($templateCode);
        $req->setTemplateParam(json_encode($params, JSON_UNESCAPED_UNICODE));

        try {
            $res = $this->client->getAcsResponse($req);
        } catch (\Throwable $e) {
            return SmsResult::transportError($e->getMessage());
        }

        return new SmsResult(
            ok: ($res->Code ?? null) === 'OK',
            requestId: $res->RequestId ?? null,
            bizId: $res->BizId ?? null,
            code: $res->Code ?? 'Unknown',
            message: $res->Message ?? '',
        );
    }
}

Bind it once in AppServiceProvider::register():

$this->app->singleton(SmsSender::class, function ($app) {
    $cfg = $app['config']->get('services.aliyun_sms');
    // Config::load(); DefaultProfile::getProfile(...); DefaultProfile::addEndpoint(...);
    return new AliyunSmsSender($client, $cfg['sign_name']);
});

Queue the send. SMS calls are 100–500 ms plus occasional timeouts. Never block a login request on Aliyun:

class SendOtpSms implements ShouldQueue
{
    use Queueable, InteractsWithQueue;
    public int $tries = 3;
    public array $backoff = [5, 30, 120];

    public function __construct(public string $phone, public string $code) {}

    public function handle(SmsSender $sms): void
    {
        $sms->send($this->phone, 'SMS_123456789', ['code' => $this->code]);
    }
}

OTP pattern. Generate → store in cache with TTL → dispatch. Verify with a constant-time comparison and increment an attempt counter on the same cache key.

$code = (string) random_int(100000, 999999);
Cache::put("otp:{$phone}", $code, now()->addMinutes(5));
SendOtpSms::dispatch($phone, $code);

Delivery audit via QuerySendDetails. After sends, persist BizId and RequestId. Support requests about "I never got the code" are answered in seconds with QuerySendDetails using the phone, date (YYYYMMDD), and BizId.

Batch sends. There is no bulk endpoint — the API is one-phone-per-call. Loop with a delay (5–10 ms), a RateLimiter, or split into a queued fan-out. Do not foreach 10k numbers synchronously.

Fake it in tests. Bind a FakeSmsSender in TestCase::setUp() that records (phone, template, params), and assert against the fake. The real client should never run in CI.

Gotchas and Tips

  • The package is effectively abandoned (last release 2017-10-19, 4 stars, 0 dependents). Pin the version in composer.json, keep the SmsSender interface boundary tight, and plan a migration to alibabacloud/dysmsapi-20170525 (official, maintained) when it hurts. The interface wrapper is what makes that a one-file change.
  • PHP 8 compatibility is not guaranteed. Old Aliyun core code uses implicit-nullable parameters and dynamic properties, both deprecated/removed in newer PHP. Test on your target version; if it breaks, either pin PHP or fork the vendor shim rather than patching inside vendor/.
  • setTemplateParam takes a JSON string, not an array. Passing an array silently produces isv.TEMPLATE_MISSING_PARAMETERS or an empty-param template. Always json_encode(...) and cast values to strings — numeric 482913 vs "482913" can behave differently in some templates.
  • SignName must be byte-identical to the approved signature, including any trailing spaces. A common failure is config drift between staging and prod sign names.
  • Error codes you will actually hit:
    • isv.BUSINESS_LIMIT_CONTROL — per-number throttling (default 1/min, 5/hour, 10/day). Show "try again later"; do not retry blindly.
    • isv.DAY_LIMIT_CONTROL — daily cap per number.
    • isv.MOBILE_NUMBER_ILLEGAL — bad number or non-mainland format.
    • isv.SMS_SIGNATURE_ILLEGAL / isv.SMS_TEMPLATE_ILLEGAL — not approved or wrong code.
    • isv.OUT_OF_SERVICE — account suspended or arrears.
    • InvalidAccessKeyId.NotFound / SignatureDoesNotMatch — wrong key or wrong region signing.
    • Throttling.User — per-account QPS; this is the only one worth exponential-backoff retrying.
  • Region matters for signatures. Set the endpoint to dysmsapi.aliyuncs.com and pass the matching region to DefaultProfile::getProfile() and addEndpoint(). A mismatched region produces SignatureDoesNotMatch, which looks like a credential bug but isn’t.
  • SSL/CA issues on older setups. If you get cURL error 60, set curl.cainfo in php.ini or pass a CA bundle — the 2017 SDK doesn’t always bundle one.
  • Never log the OTP or raw phone. Log RequestId, BizId, Code, and a masked number (138****8000). OTPs in logs are a compliance problem.
  • Timestamps in QuerySendDetails are YYYYMMDD in the account’s timezone; use Carbon::now('Asia/Shanghai')->format('Ymd'), not now() in UTC.
  • Extension point: if you need retries, signing tweaks, or extra endpoints, wrap/subclass the SDK classes in app/ — never edit vendor/ (a composer update wipes it, and this package will get reinstalled from a stale artifact).
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator