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

Dysmsapi 20170525 Laravel Package

alibabacloud/dysmsapi-20170525

Official Alibaba Cloud Dysmsapi SDK for PHP, providing a simple client for Alibaba Cloud SMS: send verification, notification, and marketing messages, query delivery status, and manage signatures and templates via the Dysmsapi 2017-05-25 API.

View on GitHub
Deep Wiki
Context7

Getting Started

This is an official, auto-generated Alibaba Cloud SDK — not a Laravel package. There is no service provider, no config file, no facade, and no testing fake. You get one client class and ~hundreds of request/response model classes under AlibabaCloud\SDK\Dysmsapi\V20170525\. Plan to wrap it yourself on day one.

1. Install

composer require alibabacloud/dysmsapi-20170525

It pulls in alibabacloud/darabonba-openapi and tea/tea (the OpenAPI runtime + Guzzle transport). PHP 7.2+ in current releases; check the tag you pin.

2. Credentials — use a RAM user with only dysms:SendSms / dysms:QuerySendDetails, never your root AK.

ALIBABA_CLOUD_ACCESS_KEY_ID=LTAI...
ALIBABA_CLOUD_ACCESS_KEY_SECRET=...
ALIBABA_SMS_SIGN=YourApprovedSign
ALIBABA_SMS_ENDPOINT=dysmsapi.aliyuncs.com

Note the endpoint is regional: mainland China uses dysmsapi.aliyuncs.com, international uses dysmsapi.ap-southeast-1.aliyuncs.com. Sending to a number that doesn't match the endpoint's region fails.

3. Where to look first (in this order)

  • src/Dysmsapi.php — the whole API surface as methods: sendSms, sendBatchSms, querySendDetails, querySendStatistics, sendSmsVerifyCode, checkSmsVerifyCode, getSmsTemplateList, getSmsSignList.
  • src/Models/SendSmsRequest.php — the required fields you'll actually touch.
  • src/Models/SendSmsResponseBody.php — what a "successful" HTTP 200 that failed looks like.

4. First send (verified working shape)

use AlibabaCloud\Darabonba\OpenApi\Models\Config;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;

$config = new Config([
    'accessKeyId'     => config('services.alibaba_sms.key'),
    'accessKeySecret' => config('services.alibaba_sms.secret'),
]);
$config->endpoint = config('services.alibaba_sms.endpoint');

$client = new Dysmsapi($config);

$request = new SendSmsRequest([
    'phoneNumbers'  => '8613800000000',
    'signName'      => config('services.alibaba_sms.sign'),
    'templateCode'  => 'SMS_123456789',
    'templateParam' => json_encode(['code' => '482913']),
]);

$response = $client->sendSms($request);

$response->body->code;    // "OK" on success, "isv.xxx" on business failure
$response->body->bizId;   // keep this; it's your receipt for QuerySendDetails

(On older tags the Config class lives at AlibabaCloud\SDK\Dysmsapi\V20170525\Config — same constructor. If the import above 404s, try that.)

First realistic use case: an OTP/login code. Approve a sign and a 验证码 template in the Alibaba Cloud console before writing code — SMS is one of the few services where you cannot test anything until a human approves your sign and template (usually minutes to a couple of hours).

Implementation Patterns

Wrap the client in a singleton behind your own interface

You will want to swap this out in tests, and you may want a fallback provider. Don't scatter new Dysmsapi(...) through controllers.

// app/Services/Sms/SmsGateway.php
interface SmsGateway {
    public function send(SmsMessage $message): SmsResult;
}
// app/Providers/AlibabaSmsServiceProvider.php
$this->app->singleton(Dysmsapi::class, function () {
    $config = new Config([
        'accessKeyId'     => config('services.alibaba_sms.key'),
        'accessKeySecret' => config('services.alibaba_sms.secret'),
    ]);
    $config->endpoint      = config('services.alibaba_sms.endpoint');
    $config->connectTimeout = 3;   // seconds
    $config->readTimeout    = 5;
    return new Dysmsapi($config);
});

$this->app->bind(SmsGateway::class, AlibabaSmsGateway::class);

Setting connectTimeout/readTimeout explicitly matters — the default is generous, and a hung SMS call inside a request will hold a PHP-FPM worker.

Always translate the response into your own result object

SendSms returns HTTP 200 even when it rejects the message. Your gateway should never leak $response->body->code to callers.

public function send(SmsMessage $m): SmsResult
{
    try {
        $res = $this->client->sendSms(new SendSmsRequest([...]));
    } catch (TeaError | TeaUnableRetryError $e) {
        return SmsResult::transportError($e->getMessage());
    }

    if ($res->body->code !== 'OK') {
        Log::warning('sms.rejected', ['code' => $res->body->code, 'msg' => $res->body->message]);
        return SmsResult::rejected($res->body->code, $res->body->message);
    }

    return SmsResult::sent($res->body->bizId);
}

Queue it, don't send inline

SMS is a network call to another continent. Push to a job so a slow/retrying send never blocks a login request.

final class SendSmsJob implements ShouldQueue, ShouldBeUnique
{
    public int $tries = 3;
    public array $backoff = [5, 30, 120];
    public int $uniqueFor = 60;

    public function handle(SmsGateway $sms): void { /* ... */ }
}

Cap $tries low; if Alibaba is throttling you, retrying harder is counterproductive (see gotchas).

Notification channel

The idiomatic Laravel hookup is a notification channel so OTPs live next to your other user-facing messages:

// app/Notifications/Channels/AlibabaSmsChannel.php
public function send($notifiable, Notification $notification)
{
    $message = $notification->toAlibabaSms($notifiable);
    app(SmsGateway::class)->send($message->to($notifiable->routeNotificationFor('sms')));
}

Your SmsMessage should hold templateCode, params (array), signName. Let the message class own the domain knowledge (SmsMessage::loginCode($code) → SMS_123456789 + ['code' => $code]), so template codes aren't littered through the codebase.

Persist every send

QuerySendDetails only retains roughly the last 7 days and is awkward to query. Write your own sms_logs table (phone, template, params JSON, biz_id, response code, provider, user_id, ip) in the same job. This becomes your audit trail, your debugging tool, and your per-user rate-limiting source.

Anti-abuse before you send

Do rate limiting in your app — per phone number, per IP, per user — before the SDK call. Alibaba's own SendSmsVerifyCode also has Interval and DuplicatePolicy options, but you still need your own limits to protect your budget and reputation.

RateLimiter::attempt("sms:{$phone}", 1, fn () => ..., 60); // 1 per minute per number

Let Alibaba manage the verification code (optional but useful)

sendSmsVerifyCode / checkSmsVerifyCode push code generation, expiry, and retry rules to the server, so you don't need Redis for the code. Good when you have a single code flow and want less infra; less good when you need the code for your own business logic or want a provider-agnostic auth layer.

Batch and marketing

sendBatchSms takes parallel arrays (phoneNumberJson, signNameJson, templateParamJson) — it's easy to get out of sync. Build them from a single collection:

$recipients = collect($list)->map(fn ($r) => [
    'phone'  => $r->phone,
    'sign'   => $r->sign ?? config('services.alibaba_sms.sign'),
    'params' => json_encode(['name' => $r->name]),
]);

// then pluck each column into its own json_encode() payload

Multi-account / multi-region

If you send to both mainland China and international numbers you need two clients with two endpoints and two sign sets. Bind a SmsClientFactory::for(string $region) rather than a single singleton, and route by phone prefix (+86 → CN, otherwise → international).

Gotchas and Tips

templateParam is a JSON string, not an array. Passing an array yields a signature/serialization error that reads nothing like "you passed the wrong type". Single-variable templates still need {"code":"1234"}, not "1234".

HTTP 200 does not mean sent. The client only throws on transport/parse errors. Business failures come back as body->code !== 'OK'. If you only catch exceptions, you will silently believe every SMS went out.

The error codes you will actually hit:

Code Meaning Retry?
isv.BUSINESS_LIMIT_CONTROL Per-number/per-day flow control No — backoff or drop
isv.MOBILE_NUMBER_ILLEGAL Bad number format No
isv.SMS_TEMPLATE_ILLEGAL / isv.SMS_SIGNATURE_ILLEGAL Not approved / mismatched No
isv.OUT_OF_SERVICE Account in arrears No
isp.RAM_PERMISSION_DENY AK lacks dysms:SendSms No
isv.AMOUNT_NOT_ENOUGH Out of credit No

Retrying BUSINESS_LIMIT_CONTROL is the classic self-inflicted outage — it escalates the throttle.

Phone number format. Mainland numbers need the 86 prefix and no + (8613800000000). International numbers use country code without +. There is no normalization done for you, and a leading + or a missing country code reliably produces MOBILE_NUMBER_ILLEGAL.

Signs and templates are region- and account-scoped. A sign approved for China cannot be used from the Singapore endpoint. When you clone config between staging and production, the template codes usually don't exist in the other account.

Testing is the sharpest edge. The SDK uses Guzzle directly through the Darabonba runtime, so Http::fake() does not intercept it. Do not try to spy on it. Instead:

  • bind a FakeSmsGateway implementing your interface in tests (this is why the interface exists), and
  • assert on your own sms_logs rows rather than on SDK responses.

Composer/autoload weight. These generated SDKs contain thousands of small model classes. Run composer dump-autoload -o in production — without it, the classmap lookups get noticeably slow on cold containers. In CI, --classmap-authoritative helps; just be aware the classmap generation step is slower than for a typical package.

Versioning is a firehose. New tags land constantly (the metadata lists a 2026 release). Pin the exact version in composer.json ("alibabacloud/dysmsapi-20170525": "3.x.y") rather than ^. Generated SDKs occasionally reshape request models between majors.

Config namespace has moved. Older versions expose AlibabaCloud\SDK\Dysmsapi\V20170525\Config; newer ones use the shared AlibabaCloud\Darabonba\OpenApi\Models\Config. If you copy a snippet from an old blog post and get a "class not found", this is why — check vendor/alibabacloud/dysmsapi-20170525/src/ for what actually exists.

Log the bizId. It is the only key that lets support trace a delivery. QuerySendDetails needs it plus sendDate (YYYYMMDD) and the phone number, and gives you SendStatus (1 waiting, 2 failed, 3 delivered). Build a "resend receipt" screen on top of it — but note the 7-day retention.

Extension point for a second provider: because everything funnels through SmsGateway, a FailoverSmsGateway that tries Alibaba then falls back to another provider is ~30 lines. Do this if SMS is in your login path — a single-provider OTP flow is a single point of account lockout.

Zero dependents (at the time of writing) is a signal, not a verdict. This is a vendor-generated package consumed as a transitive dependency of other Alibaba SDKs, so it isn't "unmaintained" — but it also means no community issues, no Stack Overflow answers, and no Laravel idioms. Read src/Models/*Request.php for the real contract; that's the only reliable documentation for each call's required fields.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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