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

Wechat Work Group Welcome Template Bundle Laravel Package

tourze/wechat-work-group-welcome-template-bundle

A PHP bundle for managing WeChat Work (企业微信) group welcome message templates. It provides services and configuration to create, organize, and reuse onboarding greetings for customer groups, helping teams keep welcome messages consistent and reduce manual setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel-native package, and it targets the WeCom (企业微信 / WeChat Work) group welcome template (入群欢迎语素材) APIs. Treat it as a thin domain wrapper around externalcontact/group_welcome_template/* and externalcontact/send_welcome_msg rather than a full SDK.

  1. Install and inspect before wiring anything up

    composer require tourze/wechat-work-group-welcome-template-bundle
    

    Then read, in this order:

    • src/*Bundle.php — the bundle class; shows what services/entities are registered.
    • src/Entity/ — the Doctrine entities that mirror WeCom template payloads (text / image / link / miniprogram / file).
    • src/Service/ or src/Repository/ — the actual API call sites; this is where the real behaviour lives.
    • The tourze/wechat-work-bundle (or equivalent) dependency — this bundle almost certainly delegates HTTP transport, access-token caching, and media_id uploads to it. Most of your configuration lives there, not here.
  2. Registration

    • Symfony: add to config/bundles.php, then import the bundle's routing/services as documented in its README/Resources/config.
    • Laravel: there is no service provider. Either register the bundle through a Symfony-bundle bridge (e.g. tuck/eloquent-bundle-style shims) or, more pragmatically, instantiate the bundle's service classes directly and inject your own HTTP client. Do not expect Laravel facades, config publishing, or a config/wechat-work.php file.
  3. Configuration you actually need: corp ID, the external-contact agent's secret, and a cache pool for the access token. Confirm where the token provider comes from — that's the single most common setup failure.

  4. First use case — the happy path worth validating end-to-end:

    • Upload the images/files you want via the WeCom media endpoint (this bundle consumes media_ids; it does not upload them for you).
    • Create a group welcome template (text + attachments) and persist it locally.
    • In your "external contact added / customer group created" callback handler, take the welcome_code from the event and call send_welcome_msg with that code and your stored template.

That last step is the whole point of the package: templates are material, and the welcome_code is a 20-minute-lived ticket to send one.

Implementation Patterns

Store templates locally, send by reference. The WeCom API has no "list templates" call that's useful at runtime — you add/edit/delete templates and get back a template_id. Mirror them in your own table (the bundle ships Doctrine entities; map them to Eloquent models if you're in Laravel) with columns for template_id, owner userid, and a type/scene discriminator. Sending then becomes a local lookup plus one API call.

Two attachment modes, pick one deliberately.

  • Default template per owner: set one template as the default for a sales rep's userid; every new group that rep joins gets it automatically. Good for uniform onboarding flows, bad for per-campaign copy.
  • Explicit send: keep templates as a pool, choose one at send time with welcome_code (added-contact events) or chat_id (group events). This is what you want for A/B copy or per-campaign variants.

Queue the send. welcome_code has a short lifetime (20 seconds from the callback). Do the send in a job dispatched immediately and not behind a backlog of other work; if the queue is cold, send synchronously in the webhook handler instead. Store the code on the job payload and mark it consumed on success — retries after expiry are pointless and should fail fast.

Idempotency key on the template side. Since add returns a new template_id every time, a naive "sync templates on boot" seeder will spam duplicates and hit rate limits. Keep a template_id in your DB, compare a content hash, and only call edit when it changes.

Wrap the bundle service, don't inject it everywhere. Put a single WelcomeTemplateSender application service in front of it:

final class WelcomeTemplateSender
{
    public function __construct(
        private GroupWelcomeTemplateService $templates, // from the bundle
        private HttpClientInterface $media,             // for uploads
    ) {}

    public function sendForNewContact(string $welcomeCode, string $ownerUserId): void
    {
        $template = $this->templates->findDefaultFor($ownerUserId);
        $this->templates->send($welcomeCode, $template); // expires in ~20s
    }
}

This gives you one seam to swap when the 0.x API moves, and one place to add logging/metrics.

Media pipeline is your responsibility. Images and files need a media_id from the WeCom media upload endpoint; those IDs are temporary (roughly three days). Cache media_id → your asset ID with a TTL, and re-upload on expiry rather than letting sends fail. Mini-program attachments use appid + page, no media upload.

Integration tip — align with your contact-event source. If you're already consuming WeCom callback XML (or a relay's webhooks), the welcome_code arrives on the change_external_contact / add_external_contact event. Keep template storage and event handling in the same module so the code → template → send story is readable in one place.

Gotchas and Tips

  • The release you're evaluating (0.0.2) has no changelog. The notes are a bare compare link between 0.0.1 and 0.0.2, so there is nothing to attribute. Treat 0.0.x as an unstable API: pin an exact version in composer.json, and after any bump diff src/Entity/ and src/Service/ before trusting your tests. Don't assume semver protection.
  • Symfony coupling is the biggest day-to-day friction in Laravel. Expect constructor-injected Symfony HttpClientInterface, EventDispatcherInterface, and Doctrine EntityManagerInterface. Plan for a small bridge/adapter layer instead of fighting it service-by-service.
  • welcome_code expiry is the #1 production failure. Twenty seconds. Never persist codes for later, never let them sit in a retry queue, and never batch them.
  • One default template per owner. Setting a new default silently replaces the old one for that userid. If your product implies "multiple defaults," enforce the choice in your own UI/domain layer.
  • Attachments are constrained: a text block carries a limited number of attachments, and each attachment is exactly one type (image/link/miniprogram/file). Mixing shapes into one field is a common source of 40058 invalid parameter errors.
  • Access token contention. WeCom invalidates previously issued tokens when a new one is fetched. If your app, a queue worker, and a cron each build their own HTTP client, you'll get random 42001 access_token expired errors. Ensure a single shared cache pool backs the token provider.
  • Rate limits are per-corp and quiet. Bulk template creation or bulk sends during onboarding batches will trip them; add jittered backoff at the job level, not inside the bundle.
  • Debugging: log the raw request body before dispatch. WeCom error codes (40001, 40058, 45009) are precise about which field is wrong; the bundle will likely surface them as a generic exception with the code string, so capture the payload yourself.
  • Extension points: the bundle's value is its entity mapping and payload serialisation. If the API surface is too thin for your needs, it's usually cheaper to keep its entities for local storage and call https://qyapi.weixin.qq.com/cgi-bin/externalcontact/... yourself for the handful of endpoints you actually use, rather than patching the bundle mid-0.x.
  • Test against a real corp. There is no meaningful sandbox for these endpoints; assert on your own stored state and treat the API call as fire-and-verify via callback events.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle