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 Official Account Material Bundle Laravel Package

tourze/wechat-official-account-material-bundle

A Symfony bundle for managing WeChat Official Account materials. It offers services, entities, and config to upload, list, and sync images, voice, video, and news items via the WeChat API, with DI integration that simplifies persistent material storage and retrieval in PHP apps.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle for managing WeChat Official Account (公众号) materials — temporary media, permanent media, and news/图文 articles — published by the Tourze collective. Version 0.1.0 is the first release with a real changelog diff against 0.0.1, but the notes are just a compare link: treat the commit history as the documentation.

composer require tourze/wechat-official-account-material-bundle:0.1.*

Because it is a bundle and not a Laravel package, there is no package auto-discovery. Wire it yourself:

  1. Read vendor/tourze/wechat-official-account-material-bundle/composer.json — check for "type": "symfony-bundle" and any symfony/framework-bundle requirement. If the latter is present, expect container/event-dispatcher baggage and prefer a thin adapter over full registration.
  2. Look in src/ for the *Bundle.php class and DependencyInjection/Configuration.php. That config tree is the only accurate reference for options — no README, no config docs.
  3. tests/ is the second place to look; tourze bundles usually have functional tests that show the expected service wiring.
  4. Register the bundle's services in a small WechatMaterialServiceProvider (bind the config array, tag the bundle class), or instantiate the manager classes directly with new if the bundle is thin.

First use case: upload a temporary image and use the returned media_id to reply to a follower message. Get appid/secret from 微信公众平台 → 开发 → 基本配置, whitelist your server egress IP, and store them in config/wechat.php:

return [
    'official_account' => [
        'app_id' => env('WECHAT_APP_ID'),
        'secret' => env('WECHAT_SECRET'),
    ],
];

Implementation Patterns

Token handling is the core pattern. Every material call needs access_token. Do not let the bundle fetch it per request — cache it and refresh on 40001/42001:

$token = Cache::remember('wechat:token:'.$appId, 7000, fn () => $client->fetchToken());

$result = $material->uploadTemp('image', $path);
if (in_array($result['errcode'] ?? 0, [40001, 42001])) {
    Cache::forget('wechat:token:'.$appId);
    // retry once with a refreshed token
}

Wrap the retry in Cache::lock("wechat:token:{$appId}") so a burst of queue workers doesn't stampede the token endpoint and invalidate each other's tokens.

Persist media_id — don't treat WeChat as your database. Add a local model:

Schema::create('wechat_materials', function (Blueprint $t) {
    $t->id();
    $t->string('media_id')->unique();
    $t->string('type');            // image|voice|video|thumb|news
    $t->boolean('permanent');
    $t->string('url')->nullable();
    $t->timestamp('expires_at')->nullable(); // temp media: ~3 days
    $t->json('meta')->nullable();
    $t->timestamps();
});

This gives you listing/search/permissions that the WeChat batchget API does not (it paginates at 20 items and is slow).

Typical flows you'll implement:

  • uploadTemp(type, file) → auto-reply media, expires in ~3 days.
  • uploadPermanent(type, file, title, introduction) → image/voice/video, then deletePermanent(media_id) when replaced.
  • uploadNews([...articles]) → 图文; each article needs a thumb_media_id that is itself a permanent thumb upload.
  • batchGet(type, offset, count) → sync job that mirrors counts into your DB nightly.

Use jobs for anything bulk. Importing a folder of images should be one job per file with ->onQueue('wechat') and rate limiting (Redis::throttle) — WeChat's per-app quotas are strict and a synchronous loop will hit 45009.

Testing: fake the HTTP layer (Guzzle mock handler or Http::fake if the bundle uses the Laravel client) and assert on the request body, especially multipart boundaries for uploads. Snapshot the JSON error envelope so a bundle upgrade that changes error shape fails loudly.

Gotchas and Tips

  • This is a Symfony bundle in a Laravel app. No extra.laravel providers, no publish tags, no facades. Budget half a day for wiring and expect to write a thin WechatMaterial adapter of your own rather than exposing bundle classes to your domain code.
  • 0.1.0 has no human-readable release notes. Pin 0.1.* and read the GitHub compare diff before bumping. Pre-1.0 tourze packages rename classes freely between minors.
  • Temporary vs permanent is the #1 footgun. Temp media_ids expire (3 days) and silently break auto-replies; permanent materials are capped (roughly 5,000 images / 1,000 news items) and users can delete them from the admin UI, orphaning your DB rows. Store the flag explicitly and reconcile.
  • WeChat returns HTTP 200 on errors. Always inspect errcode; 0 or missing means success. Common codes: 40007 invalid media_id, 40009 invalid image, 45009 API quota, 48001 API unauthorised (feature not enabled on the account type).
  • Size/format limits differ by material type and permanence (e.g. image 10 MB temp vs 2 MB permanent, thumbnail 64 KB). Validate locally before uploading — round-tripping a 12 MB file to get 40009 wastes quota.
  • Uploads are not idempotent. The same file uploaded twice yields two media_ids and two quota charges. Hash files locally and check your table first.
  • Extension points: swap the underlying HTTP client binding and the token cache implementation via the container; decorate the material service to log raw requests/responses. Log the raw Chinese errmsg — it is more specific than any mapping you'll write.
  • IP whitelist and HTTPS must be configured in the WeChat console before any call succeeds; the failure mode is an opaque 40164, not a helpful message.
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