tourze/wechat-pay-bundle
tourze/wechat-pay-bundle is a PHP bundle for integrating WeChat Pay into your application. It provides packaged configuration and service wiring for common WeChat Pay workflows, helping you add payment support with less boilerplate and a cleaner, more maintainable setup.
This package is Tourze's WeChat Pay integration bundle (微信支付). The 0.1.0 release is a pre-1.0 milestone whose changelog is published only as a GitHub compare link (0.0.3...0.1.0), so treat the diff — not a release narrative — as the source of truth for what changed.
Install and pin. Add with an explicit constraint rather than a floating range:
composer require tourze/wechat-pay-bundle:0.1.*
The package inherits Tourze's Symfony bundle conventions, so in a Laravel app you will normally register the bundle's service provider manually and publish/copy its configuration into config/ if no publish tag exists. Confirm the provider class name from composer.json (extra.laravel.providers if present) or from src/ before wiring it.
Locate the config surface first. Read the bundle's DependencyInjection/Configuration.php (or equivalent) — this is the authoritative list of merchant ID, app ID, API key, certificate paths, and signing-mode options. Documentation tends to lag behind configuration in Tourze bundles.
First use case: a single Native (QR) or JSAPI payment. Start with one payment flow end to end — create the order, render the payment parameters to the client, and receive the asynchronous notify — before touching refunds or transfers. The notify path is where most integration bugs live.
Verify credentials before writing business logic. Get a successful order query against the WeChat sandbox or a real merchant account using a throwaway script. If signing works there, everything downstream is straightforward.
Configuration over construction. Bind merchant credentials through config/services.yaml / config/wechat_pay.php and environment variables (WECHAT_PAY_MCH_ID, WECHAT_PAY_API_V3_KEY, certificate paths). Never instantiate the client with inline secrets; keeping it container-managed lets you swap it in tests.
Wrap the bundle in a thin application service. Expose your own PaymentGateway interface with methods like createOrder(), query(), refund(), and handleNotify(). The bundle sits behind it. This isolates 0.x API churn to one class and makes the rest of your domain testable.
Treat notify handlers as idempotent webhooks. Resolve the order by out_trade_no, check current status, and only transition state on a NOTPAY → SUCCESS edge. Store the raw payload plus a hash of the notification for replay detection. Return WeChat's expected acknowledgement body (XML for v2, JSON {"code":"SUCCESS"} for v3) even when you short-circuit as a duplicate.
Split synchronous and asynchronous concerns. Payment creation and querying are request-scoped and can be done inline. Refunds, transfers, and reconciliation should be queued jobs — they are slow and rate-limited, and retry semantics matter more than latency.
Use Laravel's HTTP client plumbing where the bundle allows it. If the bundle exposes a client or transport extension point, inject a configured Guzzle/Http client with timeouts, retry budget, and a logging middleware. This is the single highest-value integration hook: it gives you request/response traces without patching vendor code.
Money in fen, always. Convert at the boundary and keep integer minor units through your domain. Mixing yuan floats with WeChat's fen-based API is the most common source of off-by-100 production incidents.
Test against fakes, not the sandbox. Build a fake implementing your PaymentGateway for feature tests, and keep a small set of contract tests against the real client (skipped by default via an env flag) to catch signing regressions on upgrade.
0.x semver is not a stability promise. A 0.0.3 → 0.1.0 bump can legitimately contain breaking changes to class names, config keys, or signing defaults. Before upgrading, read the compare diff for changes under DependencyInjection/, any renamed service IDs, and any signature-algorithm defaults. Pin with 0.1.* and upgrade deliberately.
The release notes are empty of substance. There is no migration guide. Diff config/ and the public service definitions between tags; those two areas account for nearly all upgrade breakage in bundles of this shape.
Signing mode is the usual failure point. WeChat Pay v2 (MD5/HMAC-SHA256, XML) and v3 (RSA-SHA256, JSON, platform certificates) are entirely different protocols. Confirm which the bundle targets at 0.1.0 before designing around it — a mismatch here means rewriting your notify handler, not tweaking config.
Certificate handling is filesystem-sensitive. apiclient_cert.pem / apiclient_key.pem must be readable by the PHP process and must not live in the repo. Resolve paths via base_path() or an absolute env value; relative paths break between CLI (queue workers) and FPM.
Timezone and amount edge cases. WeChat timestamps are Asia/Shanghai (yyyyMMddHHmmss). If you compare notify times against now() in UTC you will silently accept stale or future-dated notifications. Parse explicitly rather than relying on app defaults.
Debugging: log the raw exchange. When a signature fails, the error message is unhelpful. Temporarily enable full request/response logging at the HTTP middleware level, then compare your canonical string against WeChat's documented ordering rules (sorted keys, &key= suffix for v2). Disable verbose logging before shipping — payloads contain order and payer identifiers.
Extension points to prefer. In rough order of safety: (1) custom HTTP client/transport, (2) event subscribers or listeners the bundle dispatches, (3) decorating the bundle's service in your container, (4) subclassing. Avoid (4) and (5) — patching vendor code — since the next 0.x release will overwrite you.
If the bundle proves too thin, it is entirely reasonable to use it only for its signing and HTTP plumbing and keep your own DTOs. At 0.1.0 the abstraction layer is likely thinner than a v1 would be; plan for your application service to carry the real weight.
How can I help you explore Laravel packages today?