tourze/wechat-mini-program-express-bundle
A Symfony bundle that integrates WeChat Mini Program express delivery features into your PHP application, providing ready-made services and configuration for WeChat logistics APIs, order and shipping workflows, and faster setup of mini program delivery functionality.
The package is tourze/wechat-mini-program-express-bundle (0.1.0, previously 0.0.2). Name and versioning tell you most of what you need before you install it:
express → WeChat Mini Program 物流/发货 APIs: querying delivery companies, uploading shipping info for an order, and receiving logistics state callbacks.bundle → this is a Symfony Bundle, not a Laravel package. There is no ServiceProvider, no config/*.php, and Composer auto-discovery will not register anything. Confirm by checking "type": "symfony-bundle" and a DependencyInjection/*Extension.php in composer.json/src.Minimal steps
composer require tourze/wechat-mini-program-express-bundle:0.1.0 — pin the exact version while it is pre-1.0.config/bundles.php only if you are in a Symfony app. For a Laravel app, see the Gotchas section before going further — this is likely a dealbreaker.src/DependencyInjection/Configuration.php and *Extension.php. That file is the authoritative list of what you must supply (app id, app secret, and typically certificate paths for sensitive endpoints).src/Service/ (or src/Api/) — that is where the actual WeChat HTTP calls live, and where the request/response shape is defined.First use case to validate the package against: fetch the list of delivery companies (delivery_list / getAllDelivery), then call the shipping-info upload for one test order. If those two calls work end-to-end, the package is doing its job; if you find yourself writing raw HTTP anyway, stop and use the underlying WeChat SDK directly.
Wrap, don't call from controllers. The bundle gives you a service; inject it into one application-level class (e.g. App\Services\Shipping\WechatExpress) that speaks your domain language (markShipped(Order $order, string $trackingNo, string $companyCode)). Everything downstream — queueing, retries, logging, idempotency — then lives in one place instead of being sprinkled across controllers.
Persist the tracking mapping yourself. Do not let a third-party bundle own your order state. Store company_code, tracking_no, and an upload status on your own model, and keep the bundle's response as a reference blob (raw JSON column) for debugging.
Queue the upload. WeChat API latency is variable and the call is a network dependency in your fulfilment flow. Dispatch a job after the "shipped" state transition, with a small backoff, and make the job idempotent by guarding on your own uploaded_at / upload_status column rather than trusting the remote call.
Handle the callback path explicitly. Express state changes arrive as a push notification. Route it to a controller that (a) verifies the signature, (b) acknowledges fast with the expected success body, and (c) pushes the actual processing onto a queue. WeChat will retry aggressively if you return anything else, so an unhandled exception in that controller turns into duplicate callbacks.
Test with a mocked HTTP client, not the real API. If the bundle is built on Symfony HttpClient (check the constructor signature of the service), MockHttpClient lets you assert the request payload without a Mini Program account. Build fixtures for success, errcode != 0, and timeout.
Configuration via env, not YAML literals. Certificates and secrets belong in .env and get injected into the bundle config. Anything committed to config/packages/ should be a %env(...)% reference.
The changelog is empty. 0.1.0 ships with nothing but a compare link (0.0.2...0.1.0). You must read the diff yourself before upgrading. On a 0.x package there is no BC promise — a minor bump from 0.0.2 to 0.1.0 can rename services, change method signatures, or alter the config tree with no deprecation path. Pin "tourze/wechat-mini-program-express-bundle": "0.1.0" and review the diff deliberately.
Laravel integration is the main risk. There is no realistic path to dropping a Symfony Bundle into a Laravel app without a bridge (binding services manually, replicating the extension's config into config/). Before committing, check whether the package's classes are plain services you can new up yourself — if so, instantiate them directly and skip the bundle. If they are wired through a container with compiler passes and tagged services, treat this as "Symfony-only" and budget accordingly.
Expect a large transitive tree. Tourze packages are typically split into many micro-packages (*-contract, *-enum, *-entity, *-doctrine-*). Run composer why on anything that surprises you after install, and expect Doctrine ORM to arrive as a dependency.
Doctrine entities mean schema coupling. If the bundle ships doctrine entities/repositories, you are inheriting a schema, not just an API client. Check whether migrations are provided or whether you are expected to map the entities onto your existing tables. In a Laravel app with Eloquent, this is usually the point where the package stops being worth it.
access_token is per-app and global. WeChat issues one token per app id with a ~2h TTL and strict rate limits on the fetch endpoint. If the bundle fetches a fresh token per request, you will hit 45009/40001 errors under load. Verify it uses a PSR-6/PSR-16 cache pool, and that the pool is shared across workers — an in-memory pool in a multi-process PHP-FPM setup is effectively broken.
Errors arrive with HTTP 200. WeChat returns failures as errcode/errmsg in the JSON body. If your code only checks $response->getStatusCode(), every failure will look like a success and you will discover it in production order data. Assert on errcode explicitly in your wrapper, and log the raw body on non-zero.
Certificates for sensitive endpoints. Refund/transfer-adjacent APIs need apiclient_cert.pem + apiclient_key.pem. Never commit them; mount via a secret and reference by path in config. A common failure is a relative path that resolves correctly in local dev and breaks in the container.
Debugging: log the full outbound request and inbound response at the wrapper boundary (masked secrets), and keep a Mini Program test account/sandbox for the callback flow — you cannot trigger real logistics state changes on a production app id on demand.
Extension points: in Symfony, decorate the bundle's service rather than forking it (services.yaml → decorates:), and listen on the dispatcher if it emits events for request/response. That keeps you upgrade-safe against the next 0.x bump.
How can I help you explore Laravel packages today?