hedeqiang/ten-im
Tencent Cloud IM (Instant Messaging) SDK wrapper for Laravel, making it easy to integrate Tencent's IM services into your application. Provides a simple, framework-friendly interface for common IM operations such as account management, messaging, and group handling, so you can add real-time chat fea...
Install the package and publish its configuration:
composer require hedeqiang/ten-im
php artisan vendor:publish --provider="Hedeqiang\TenIM\ServiceProvider"
This creates config/ten-im.php. Populate it in .env with values from the Tencent Cloud IM console (Application → Basic configuration and Account management):
TEN_IM_SDK_APP_ID=1400xxxxxx
TEN_IM_KEY=your_secret_key
TEN_IM_IDENTIFIER=admin
TEN_IM_EXPIRE=86400
identifier is the admin IM account created in the console, not a random user — all backend requests are authenticated as this account, so it must have permission for the endpoints you call.
First use case — generating a usersig so a client SDK can log in:
use Hedeqiang\TenIM\Facades\TenIM;
$sig = TenIM::genSig($userId);
// return ['usersig' => $sig, 'sdkappid' => ..., 'identifier' => $userId]
That single call is what most teams actually need 90% of the time: your Laravel auth layer verifies credentials, then hands the client a signed usersig to pass to tim.login({userID, userSig}). From there, call any server-side REST endpoint through the same facade — e.g. importing accounts, creating groups, or sending system messages.
Thin controller, fat service. Wrap the facade behind your own service so you can mock it and centralize error handling:
class ImService
{
public function userSig(string $userId): string
{
return TenIM::genSig($userId);
}
public function importAccount(string $userId, string $nick = '', string $avatar = ''): array
{
return TenIM::account_import([
'UserID' => $userId,
'Nick' => $nick,
'FaceUrl' => $avatar,
]);
}
}
Cache usersigs. They are valid for TEN_IM_EXPIRE seconds and are deterministic within that window. Cache with a safety margin under the TTL and key by user id:
public function userSig(string $userId): string
{
return Cache::remember(
"tim:usersig:{$userId}",
now()->addSeconds(config('ten-im.expire') - 300),
fn () => TenIM::genSig($userId)
);
}
Sync users on registration/profile update. Hook your User model events into account_import (idempotent — Tencent overwrites profile fields). Fire the import in a queued job so a Tencent hiccup doesn't block your registration flow.
Bulk operations. For batch account imports, chunk your user list (the API caps batch size) and dispatch one job per chunk with a slight delay to stay inside rate limits.
Group lifecycle. Create a group when a team/room is created in your app, and store Tencent's GroupId on your model. The server-side group APIs (create_group, add_group_member, destroy_group) are the standard pattern for keeping your domain model and the IM group in lockstep.
Send server-side messages for notifications. Use the REST send-message endpoint for system notifications (order shipped, invoice ready) rather than the client SDK — you control the sender identity (identifier) and audit trail.
Testing. Because the package ultimately issues HTTP calls against https://console.tim.qq.com, bind a fake in tests or swap your ImService for a mock. Avoid hitting the live API in CI.
Pin the version. Last release was July 2023 — specify an exact constraint ("hedeqiang/ten-im": "1.x.y") so a silent transitive dependency change doesn't break you. With 0 dependents, expect to read the source when something behaves unexpectedly.
Check ErrorCode, not HTTP status. Tencent IM returns 200 OK with ActionStatus: "FAIL" on business errors. Always inspect ErrorCode/ErrorInfo even when the call succeeds at the transport level:
$res = TenIM::account_import($payload);
if (($res['ActionStatus'] ?? '') !== 'OK') {
throw new ImException($res['ErrorInfo'] ?? 'unknown', $res['ErrorCode'] ?? -1);
}
Clock skew breaks signatures. The usersig embeds a timestamp; if your server clock drifts more than a few minutes, logins silently fail. Run NTP on app servers.
Admin permissions are per-endpoint. identifier in config/ten-im.php must hold the corresponding console privileges. A missing permission shows up as a generic failure on that endpoint only — check Account management → admin account → permissions first when debugging a single failing method.
Don't log the payloads. TEN_IM_KEY and returned usersig values are credentials; scrub them from Laravel logs, Sentry, and Telescope exports.
Rate limits are per API and per app. genSig is local (no network), but account/group/message endpoints are rate limited. Add retry-with-backoff around ActionStatus: "FAIL" responses that carry a rate-limit ErrorCode (usually -1 or 10002-ish depending on endpoint) rather than retrying all failures blindly.
Wrap the HTTP layer for timeouts. The package delegates to an HTTP client; set a connect_timeout/timeout at the Guzzle/Http level rather than relying on defaults, especially inside request cycles.
Callback verification isn't included. When Tencent calls your server-side callback (e.g. after-user-login hooks), you must verify the signature yourself — the package is a client SDK and won't do it.
Fall back to raw REST if needed. The REST surface is stable and documented; when the package lags a new endpoint (releases stopped in 2023), hit https://console.tim.qq.com/v4/{svc}/{cmd} directly and reuse the package only for genSig. Keeping the signature generation behind your ImService makes this migration trivial.
How can I help you explore Laravel packages today?