tourze/wechat-official-account-draft-bundle
A Symfony bundle for managing WeChat Official Account drafts. It wires the WeChat draft APIs into your Symfony app, letting you create, list, update, and delete official account draft articles through clean, configurable services so content can be staged in WeChat before publishing.
tourze/wechat-official-account-draft-bundle is the WeChat Official Account (公众号) draft box / 草稿箱 API binding — creating, listing, updating and deleting drafts, plus the handoff to the publish (freepublish) endpoints. Version 0.0.1 is the first tagged release with an empty changelog, so treat this as "source is the documentation".
Minimal steps:
composer require tourze/wechat-official-account-draft-bundle:0.0.1
Then, before writing any code, read the repository in this order:
composer.json — required PHP version, whether it depends on symfony/http-client, a PSR-18 client, or tourze/wechat-official-account-bundle (the access-token layer).src/ — look for the request/response object pairs (*Request, *Response) and any *Bundle.php / *ServiceProvider. Note: Tourze's *-bundle packages are conventionally Symfony bundles. Confirm whether Laravel auto-discovery is wired up; if not, you instantiate the request classes directly and inject your own HTTP client.First use case to validate the integration end-to-end: create a single draft from one article and confirm you get a media_id back, then list drafts and find it. That exercises auth, serialization, and response parsing in one shot. Do not start with publishing — publishing consumes a quota and is harder to undo than a draft.
// Shape is indicative — verify class names against src/ first.
$response = $client->send(new AddDraftRequest([
'articles' => [[
'title' => 'Hello',
'author' => 'team',
'content' => $html,
'thumb_media_id' => $thumbMediaId, // permanent image material
'content_source_url' => 'https://example.com/post',
'need_open_comment' => 0,
]],
]));
$mediaId = $response->getMediaId();
Bind it once, inject everywhere. Register the client/request factory as a singleton in a service provider so configuration (app id, secret, token source) lives in config/services.php and not in call sites. Wrap the bundle rather than calling it from controllers — a thin WeChatDraftService of your own means an eventual 0.0.x breaking change is a one-file fix.
Treat media_id as your primary key, not the article. Persist a drafts table: media_id, local content id, thumb_media_id, status (draft / submitted / published), submitted_at, plus the raw response JSON. Drafts are the unit WeChat's API addresses; your database should mirror that.
Pipeline it. The realistic day-to-day workflow is a multi-step job, not a single call:
thumb_media_id (permanent material API).<img src> in the article HTML to WeChat-hosted URLs (see Gotchas).draft/add → store media_id.freepublish/submit with the media_id, then poll freepublish/get for status.Model steps 3–5 as queued jobs with $tries and exponential backoff; WeChat's API is rate-limited and transiently flaky.
Token handling is the seam to get right. If the bundle accepts an injected token provider, wire it to a cached, single-flight token store (Cache::lock around refresh) shared with every other WeChat package in your app. If it manages tokens internally, check whether it refreshes on 40001/42001 or just fails — this determines whether you need a retry decorator.
Testing. Put an interface in front of the client and fake it in tests. If the bundle is built on Symfony HttpClient, MockHttpClient with recorded fixtures is the cheapest way to get coverage; if it uses Laravel's Http facade, Http::fake() works directly. Assert on errcode handling, not just the happy path.
0.0.1 is an unstable contract. Pin the exact version ("0.0.1", not ^0.0.1) and read the diff before every bump. The empty changelog means no BC promise has been stated.$app->register(...) manually, or you may find the bundle cannot be resolved by Laravel's container at all and must be instantiated by hand.errcode/errmsg in the decoded body. A response wrapper that only checks status codes will silently swallow 40007 (invalid media_id) and 45009 (rate limit).thumb_media_id must be a permanent image material, not a temporary one, and not a URL. Upload it first; temporary media expires after three days and the draft will fail to publish later.src before the draft call — this is the single most common "it worked in staging" bug.40164. Containerized/ephemeral egress IPs are a recurring trap.40001 errors everywhere.final — if they are, plan to wrap rather than extend.How can I help you explore Laravel packages today?