mrgoon/aliyun-sms
A Laravel integration for Aliyun (Alibaba Cloud) SMS that simplifies sending verification codes, notifications, and transactional messages. Configure your credentials and send SMS through a clean, familiar Laravel API with minimal setup.
1. Verify it will install at all. This package's last release was October 2017 and it targets the Aliyun SMS API as updated in May 2017. Before anything else, run the install and watch for dependency conflicts:
composer require mrgoon/aliyun-sms
On PHP 8.x or Laravel 9+ you will often get resolution errors (old guzzlehttp/guzzle or illuminate/support constraints). If so, either pin the package into an older app, or — more realistically — treat the source as a reference implementation and lift the signing logic into your own service class. The underlying protocol is small: SendSms against dysmsapi.aliyuncs.com, version 2017-05-25, signed with HMAC-SHA1.
2. Register the provider (Laravel < 5.5 only). On 5.5+ auto-discovery usually handles it; otherwise add to config/app.php:
'providers' => [
Mrgoon\AliyunSms\AliyunSmsServiceProvider::class,
],
'aliases' => [
'AliyunSms' => Mrgoon\AliyunSms\Facades\AliyunSms::class,
],
3. Publish and fill the config.
php artisan vendor:publish --provider="Mrgoon\AliyunSms\AliyunSmsServiceProvider"
// config/aliyunsms.php
return [
'access_key_id' => env('ALIYUN_SMS_KEY_ID'),
'access_key_secret' => env('ALIYUN_SMS_KEY_SECRET'),
'sign_name' => env('ALIYUN_SMS_SIGN_NAME'), // e.g. 某某科技
'region_id' => 'cn-hangzhou',
];
Put the credentials in .env, never in the committed config. Create a RAM sub-account in the Aliyun console scoped to AliyunDysmsFullAccess rather than using your root AccessKey.
4. First use case: SMS login code. This is 95% of what people use the package for. Look first at src/AliyunSms.php — the single public method is the send call, and the exact parameter order is worth confirming against the source before you wire it up. The canonical shape is:
use Mrgoon\AliyunSms\Facades\AliyunSms;
$result = AliyunSms::sendSms(
'13800138000', // PhoneNumbers
config('aliyunsms.sign_name'),
'SMS_123456789', // TemplateCode from the console
['code' => '482913'] // TemplateParam
);
A successful call returns Code === 'OK' plus RequestId, Message, and BizId. Anything else is an error — see Gotchas.
Wrap the facade behind your own service. Never let AliyunSms:: leak into controllers. A thin SmsService with sendCode(string $phone): string gives you one place to swap implementations, add logging, and fake in tests.
interface SmsSender {
public function send(string $phone, string $templateCode, array $params): bool;
}
Bind AliyunSmsSender to it in a service provider. In tests, bind a FakeSmsSender that records calls — you do not want your suite hitting Aliyun.
OTP workflow. Generate a 6-digit code, cache it, then send:
$code = (string) random_int(100000, 999999);
Cache::put("sms:code:{$phone}", $code, now()->addMinutes(5));
Cache::put("sms:throttle:{$phone}", true, now()->addSeconds(60));
$ok = $this->sms->send($phone, config('sms.templates.login'), ['code' => $code]);
Verify with a constant-time comparison and delete the key on success so codes are single-use. Store an attempt counter in the same cache entry to block brute-forcing.
Keep template codes in config, not in code. Templates are environment-specific (test vs. production Aliyun accounts have different SMS_xxx IDs), so:
// config/sms.php
'templates' => [
'login' => env('SMS_TPL_LOGIN'),
'order' => env('SMS_TPL_ORDER'),
],
Queue the send. SMS is an external HTTP call with real latency and a hard rate limit; dispatch a job rather than blocking the request. ShouldQueue + a retries = 2 is usually right — more retries just burn quota on a number that is already throttled.
Pass OutId for tracing. The 2017-05 API accepts an OutId field; set it to your internal message ID or order ID so you can reconcile Aliyun's delivery receipts with your own logs. This package may or may not expose it — if not, that is your strongest argument for writing the 30-line client yourself.
Non-Laravel usage. Instantiate the class directly with credentials and region; skip the facade entirely. Good for CLI scripts and legacy apps, but then you lose config caching, so pass credentials from your own env loader.
Use the notification channel for user-facing messages. If you already send mail/database notifications, a custom SmsChannel lets you write via() => ['sms'] and keep recipient logic in one place. Reserve the direct facade call for auth codes where you control the payload precisely.
TemplateParam variable names must match the console exactly. Aliyun validates against the approved template's variables. ['code' => ...] when the template says ${code} is fine; ['Code' => ...] or ['verify_code' => ...] fails with isv.INVALID_PARAMETERS. Symptom: works in the Aliyun console's test tool, fails from code.
The sign name must be approved and byte-identical. A pending or mismatched sign name yields isv.SMS_SIGNATURE_ILLEGAL. Whitespace and full-width/half-width characters matter.
HTTP 200 does not mean success. Aliyun returns a JSON body with a Code field; errors like isv.BUSINESS_LIMIT_CONTROL (rate limited), isv.MOBILE_NUMBER_ILLEGAL, and isv.OUT_OF_SERVICE (account in arrears) all come back as normal responses. Always check $result->Code === 'OK' explicitly and log the RequestId — support will ask for it.
Per-number and per-template throttling. Aliyun enforces roughly 1/minute per number per template and 5/minute per number overall, plus a daily cap. Your own rate limiter in front of it should be stricter than you think; users double-tapping the "send code" button is the #1 way to hit isv.BUSINESS_LIMIT_CONTROL.
This package is unmaintained. Last release 2017, no dependents of note. There is no retry, no typed responses, and possibly no PSR-18 client. Budget for replacing it: the official alibabacloud/dysmsapi-20170525 SDK or a maintained community wrapper will serve you better long-term. If you keep it, pin the version and add an integration test that actually sends to a whitelisted test number in CI-staging.
Auto-discovery + facade caching. After composer require, run php artisan config:clear — a stale cached config is the usual cause of "credentials are set but it says AccessKey is empty." Also note config:cache will freeze your env values; make sure .env is correct before deploying.
Region is fixed. SMS lives in cn-hangzhou; setting another region produces signature errors that look like credential problems. Leave it alone.
Log every send attempt with phone, template, result code, and RequestId, but mask the middle of the phone number in application logs. You will need this for abuse investigations, and Aliyun's console only retains records for a limited window.
Extension points are thin. There is no driver abstraction or event system here. The right extension point is not the package — it is your own SmsSender interface. Wrap it on day one and you can migrate to the official SDK in an afternoon instead of a sprint.
How can I help you explore Laravel packages today?