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

Qcloudsms Php Laravel Package

qcloudsms/qcloudsms_php

A PHP SDK for Tencent Cloud SMS (Qcloud SMS). Send single or bulk SMS messages, manage signatures and templates, and integrate Tencent Cloud's SMS API into Laravel or any PHP project via a simple, lightweight client.

Deep Wiki
Context7

Getting Started

composer require qcloudsms/qcloudsms_php

The package exposes a single namespace, Qcloud\Sms, with no service provider, no config file, and no facade. Everything is a plain PHP class you new up yourself. Read src/SmsSingleSender.php and src/SmsSenderUtil.php first — those two files are effectively the whole SDK; the rest are siblings (multi-send, voice, status pullers) that share the same shape.

Before writing code you need three things from the Tencent Cloud SMS console:

  1. AppID / AppKey — from the SMS application. Note this is the legacy AppKey auth model, not your SecretId/SecretKey.
  2. Sign (短信签名) — the text prefix, must be approved before you can send.
  3. Template ID (模板 ID) — content with {1}, {2} placeholders, also must be approved.

A minimal first send:

use Qcloud\Sms\SmsSingleSender;

$sender = new SmsSingleSender(config('services.qcloudsms.appid'), config('services.qcloudsms.appkey'));

$result = json_decode(
    $sender->sendWithParam(
        '86',            // nation code, no "+"
        '13800000000',
        1234567,         // template id
        ['1234', '5'],   // fills {1}, {2}
        'MyApp',         // sign
        '',              // extend
        'order-42'       // ext — your correlation id
    ),
    true
);

if (($result['result'] ?? -1) !== 0) {
    // failed
}

The first use case in almost any Laravel app is verification codes / OTP, so get that path working end-to-end (including the result check) before wiring it into a notification channel.

Implementation Patterns

Bind a service, don't scatter new calls. The SDK is stateful (appid/appkey in the constructor) and there's no container auto-resolution. Create a thin wrapper:

// config/services.php
'qcloudsms' => [
    'appid'  => env('QCLOUDSMS_APPID'),
    'appkey' => env('QCLOUDSMS_APPKEY'),
    'sign'   => env('QCLOUDSMS_SIGN'),
],

// app/Services/QcloudSmsClient.php
class QcloudSmsClient
{
    public function sendTemplate(string $phone, int $templateId, array $params, string $ext = ''): array
    {
        $result = (new SmsSingleSender(config('services.qcloudsms.appid'), config('services.qcloudsms.appkey')))
            ->sendWithParam('86', $phone, $templateId, $params, config('services.qcloudsms.sign'), '', $ext);

        return json_decode($result, true) ?? ['result' => -1, 'errmsg' => 'invalid response: '.$result];
    }
}

Bind it as a singleton and inject it — that gives you one place to add logging, retries, and result-code mapping.

Wrap in a queued job. SMS is a network call with a hardcoded 30s timeout; never do it inline in a request that a user is waiting on. dispatch(new SendSms($phone, $templateId, $params))->onQueue('sms'), then call the client. Put a tries = 3 and backoff() on the job, but only retry on transport errors — retrying on result != 0 for codes 1006/1013 will just make the rate limit worse.

Use $ext as your correlation id. It echoes back in the response, so it's the only cheap way to reconcile a send with an order/user/request. Pass the same value on every send and log it.

One class per send mode. The SDK ships SmsSingleSender, SmsMultiSender (same content to many numbers — max 200, batch), voice senders (SmsVoiceVerifyCodeSender, SmsVoicePromptSender, TtsVoiceSender, FileVoiceSender), and status pullers (SmsStatusPuller, SmsMobileStatusPuller). Don't try to unify them behind one method; expose distinct typed methods on your client and let callers pick.

Laravel notification channel. Map a Notification to a channel class that calls your QcloudSmsClient, translating toArray() params into template placeholders. Since templates are fixed in the console, pick the template ID from config per notification class rather than hardcoding it in the channel.

Status polling is a cron, not a request hook. SmsStatusPuller::pullStatus($maxNum) / pullCallback($maxNum) return a batch of delivery reports. Run them from a scheduled command with a cursor so you don't reprocess, and write the sids to a sms_logs table you created when sending.

Testing. There is no sandbox. Stub your own QcloudSmsClient in tests ($this->mock(QcloudSmsClient::class)) and reserve the real call for a single integration test gated on an env flag. Don't let SmsSenderUtil leak into test code.

Gotchas and Tips

send() returns a JSON string, not an array. This is the #1 stumble. Always json_decode($result, true) and check result === 0. If the transport fails you can get false back from httpPost, and json_decode(false) is null — guard for it.

AppID/AppKey ≠ SecretId/SecretKey. The SDK implements the old sha256("appkey=...&random=...&time=...") signing against yun.tim.qq.com/v5/.... Tencent's current API is v20210111 with TC3-HMAC-SHA256, and new accounts may only be issued SecretId/SecretKey. Verify your credentials work in the console before debugging code — otherwise every call returns signature errors (1002/1003/1004) and you'll chase ghosts.

The package is effectively unmaintained. Last release is years old, it predates PHP 8, and it targets the deprecated API. It still runs on PHP 8.x, but treat it as vendored code: fork it or copy SmsSenderUtil/SmsSingleSender into app/Support/ if you need to patch (e.g. to change timeouts or SSL options). Composer patching (cweagans/composer-patches) is the clean middle ground.

Timeouts and SSL are hardcoded inside SmsSenderUtil::httpPost. You cannot configure connect/read timeout or CA bundle from outside. Audit that method — if CURLOPT_SSL_VERIFYPEER is disabled, that's a security issue for outbound production traffic, and the only fix is forking. Also add a job-level timeout so a stuck curl doesn't pin a queue worker.

Template and sign must match exactly. Placeholder count and order must equal the $params array length, and {1} is 1-indexed. A mismatch returns a param/template error, not a validation error at call time — so validate count($params) against a config map you keep for each template ID.

Phone formatting is on you. nationCode is a string without + ('86'), and the number must not include the country code or leading zeros handling quirks. Normalize to E.164-ish input, then split. For anything outside mainland China, confirm the account has international sending enabled — otherwise it's a permission error that looks like a bad number.

Rate limits are enforced server-side and will surprise you. Roughly 30s per number for the same template, 1/min for identical content, plus daily caps per sign. Cache a short lock (Cache::lock("sms:$phone:{$templateId}", 30)) before sending so you fail fast locally instead of burning quota on rejected calls, and map code 1006/1013 to a friendly "try again later" rather than a retry.

Result codes to handle explicitly: 0 success; 1002/1003/1004 credentials/permission (never retry, alert); 1006/1013 frequency limit (retry with backoff or drop); 1007 blacklist (never retry); 1009 bad number (mark the user's phone invalid); 1014/1015 template/sign not approved (config bug, alert); 1011 insufficient balance (alert). Everything else → log full payload and drop.

Log the raw response. The SDK gives you sid and fee, which are the only way to reconcile against Tencent's billing and delivery reports later. Store sid, ext, phone, template id, and raw JSON. Don't log the appkey.

Extension points are essentially zero. No events, no middleware, no config. The integration seam is your own wrapper class — keep it free of SDK types in its public signature (return a small SmsResult DTO) so that when you eventually migrate to the v3 API or a maintained SDK, only one file changes.

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