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

Tencent Cloud Sms Bundle Laravel Package

tourze/tencent-cloud-sms-bundle

A Symfony bundle that integrates Tencent Cloud SMS, providing ready-made service configuration and a simple client so you can send verification codes, notifications, and other text messages through Tencent Cloud from your Symfony application with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: this is a Symfony bundle (tourze/*-bundle naming, DependencyInjection/, *Bundle.php), not a Laravel package. At 0.0.1 there is no README, no usage docs, and the only release artifact is a changelog link. Treat everything below as "read the source before trusting it," not "follow the docs."

  1. Read the package before installing it.

    • composer.json — check type, require (expect symfony/framework-bundle, tencentcloud/tencentcloud-sdk-php or a hand-rolled signer, tourze/* helper bundles), and the autoload PSR-4 prefix.
    • src/ — find the single service class that actually calls Tencent (usually named *Service, *Client, or *sms). That one file is the entire API surface.
    • DependencyInjection/Configuration.php + Extension.php — this is where config keys live. If Configuration.php is missing, there is no config tree and everything is hardcoded or env-driven; that changes your integration plan entirely.
    • config/services.yaml (or PHP/attributes) — confirms whether services are public (needed for app()->make() in Laravel) or private/autowired.
  2. Install into a Laravel app.

    composer require tourze/tencent-cloud-sms-bundle
    

    Then decide how you register it. Two options:

    • Bring in a Symfony-bundle bridge (e.g. laravel/symfony-bundle-bridge-style packages) and register the bundle in bootstrap/providers.php.
    • Skip the bundle entirely: instantiate the underlying service class in a small Laravel service provider and bind it into the container yourself. For a 0.0.1 package with no docs, this is usually the lower-risk path — you get the SDK wrapper without inheriting Symfony kernel assumptions.
  3. Credentials. Expect TENCENTCLOUD_SECRET_ID, TENCENTCLOUD_SECRET_KEY, plus SMS-specific SDK_APP_ID, SIGN_NAME, and TEMPLATE_ID. Confirm the exact env var names in Configuration.php — do not assume the TENCENTCLOUD_* prefix, tourze packages are inconsistent about this. Put them in config/services.php and .env, never in a committed file.

  4. First use case: send one verification code to your own phone, in a tinker session, before wiring anything else.

    $sms = app(\Tourze\TencentCloudSmsBundle\Service\SmsService::class);
    $result = $sms->send('+8613800000000', '123456', 'your-template-id');
    

    If the class name or method signature differs, that's expected — grep the source for SendSms / SendStatus in the Tencent SDK call to find the real entry point.

Implementation Patterns

Wrap it, don't call it inline. The bundle's service is a thin transport. Put a SmsSender interface in your app, implement it with the bundle's service, and let your domain code depend on the interface. This buys you a LogSmsSender for local/testing and a NullSmsSender for CI, and it means a 0.0.2 breaking rename costs you one file.

Bind it explicitly in a provider. Regardless of whether the bundle auto-registers:

$this->app->singleton(SmsSender::class, fn ($app) => new TencentSmsSender(
    $app->make(\Tourze\TencentCloudSmsBundle\Service\SmsService::class),
    config('services.tencent.sms'),
));

Explicit binding is the only way to be sure the service is resolvable — private Symfony services are not reachable from app().

Drive it from a queued job, not from the controller. SMS is a network call with 100–2000 ms latency and an external failure mode. The pattern that holds up:

  1. Controller validates the phone number, creates a SendVerificationCode job.
  2. Job generates the code, stores the hash + expiry + attempt count in cache/DB first, then sends.
  3. Job uses $tries = 3, backoff() of [5, 30, 120], and overrides retryUntil().
  4. Only clear/overwrite the stored code after a successful send, so a failed send doesn't burn the user's attempt.

Store the code with intent, not just the value. cache()->put("sms:code:{$phone}", ['hash' => Hash::make($code), 'attempts' => 0], 300) — you need attempt counting and a consumed flag to build sane verification later. The bundle will not do any of this for you; it is strictly a sender.

Template-per-purpose mapping. Keep a config map of logical names to Tencent template IDs:

'templates' => [
    'login'     => env('SMS_TPL_LOGIN'),
    'order_paid'=> env('SMS_TPL_ORDER_PAID'),
],

Tencent template params are positional (['123456', '5']), so centralize the ordering in one class per template rather than at each call site. Positional params are the single most common source of "message sent, wrong content" bugs.

Rate-limit at the edge. A RateLimiter::for('sms', fn ($job) => Limit::perMinute(1)->by($job->phone)) in the job's middleware, plus a per-IP limit on the controller. Nothing in the bundle throttles for you.

Normalize phone numbers before they reach the SDK. Tencent expects E.164 with the +86 prefix for mainland numbers. Run everything through one normalizePhone() helper (+86, strip spaces/dashes, strip a leading 0) and reject anything that doesn't match ^\+[1-9]\d{7,14}$. Do it once, at the boundary.

Gotchas and Tips

0.0.1 means the API is not stable. Pre-1.0 tourze packages routinely rename classes and change method signatures between patch releases. Pin exactly ("tourze/tencent-cloud-sms-bundle": "0.0.1") and read the diff before any bump. There is no changelog content beyond a compare link, so the diff is the changelog.

Symfony bundle ≠ Laravel package. Watch for these when evaluating whether it will drop in:

  • Services defined as private — invisible to app().
  • Configuration.php using Symfony config semantics (env placeholders, %env()%, prototype arrays) that Laravel's config() won't populate.
  • Bundles that inject EntityManagerInterface or depend on tourze/doctrine-* — that drags Doctrine into a Laravel app, which is a hard no.
  • Autoloaded attribute-based config (#[Autoconfigure], #[AsAlias]) — silently ignored outside a Symfony kernel with attribute autoconfiguration enabled.

Check require in composer.json for doctrine/* and symfony/* before committing.

Env/config quirks. If Configuration.php declares env vars with defaults of null and the service doesn't guard against null, you get a signature/auth error from Tencent rather than a clear "missing config" exception. Fail fast in your own provider: validate secret_id, secret_key, sdk_app_id, sign_name on boot and throw in register()/boot().

Error handling is on you. The Tencent SDK returns a response object with SendStatusSet[0].Code; "Ok" is success, everything else is a failure, and Response.Code covers request-level errors (bad signature, expired key, insufficient balance). A wrapper that returns the raw SDK object leaks that structure into your domain. Map it to a small result object, and specifically distinguish:

  • FailedOperation.PhoneNumberInBlacklist — do not retry, tell the user.
  • LimitExceeded.PhoneNumberDailyLimit / PhoneNumberThirtySecondLimit — do not retry, back off.
  • AuthFailure.* — retry is pointless; alert.
  • InternalError.*, timeouts, RequestLimitExceeded — safe to retry.

Retrying a blacklist or daily-limit error three times just burns your quota and your queue workers.

Debugging. Set the SDK's HTTP client to log request/response bodies in non-production (tencentcloud SDK supports a HttpProfile with reqTimeout/endpoint; the bundle may or may not expose it). If it doesn't, you often have to reach inside the service via reflection or reconstruct the client yourself. Also enable Guzzle logging via HttpClientInterface if the package accepts one — check the constructor. If the constructor takes no injectable client, that's your signal that testability was not a design goal, and you should wrap it in your own adapter.

Testing. Never let a test hit Tencent. Bind a fake in TestCase::setUp() and assert on the intent (sms was sent to +86138..., template login, params [code, 5]). With no injectable HTTP client in the bundle, Http::fake() won't help because the SDK uses Guzzle directly — use a fake implementation of your own interface instead, which is another reason to wrap it.

Extension point you actually want. The one thing worth digging for in DependencyInjection/ is whether the bundle exposes a tagged interface or an alias ($container->setAlias(SmsService::class, ...)) you can rebind. If it does, you can swap implementations cleanly in config/services.yaml. If it hardcodes new SmsClient(...), fork or wrap — don't fight it.

Cost and quota guardrails. Add a daily counter (cache or DB) per template and a global circuit breaker. An unthrottled send path plus a retry policy plus Tencent's per-template limits is how teams discover their SMS bill. Also confirm the account has a spending cap configured on the Tencent side — the bundle certainly won't set one for you.

Upgrade watchlist. When 0.0.2 lands, diff for: namespace changes, constructor signature changes, added required config keys, and dropped Symfony version constraints. Given the lack of documentation, keeping a local fork or a thin adapter layer is cheaper than chasing renames.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager