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

Cainiao Pickup Bundle Laravel Package

tourze/cainiao-pickup-bundle

A PHP bundle that integrates Cainiao pickup services into your application, offering configuration and service wiring for parcel pickup workflows. Simplifies connecting your project to Cainiao logistics endpoints with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require tourze/cainiao-pickup-bundle

This is the very first tagged release (0.0.1), so treat it as experimental: there is no changelog body, no upgrade guide, and no stability guarantee. Pin the version exactly ("0.0.1") until you've verified behaviour in your own environment.

Where to look first (in this order, because documentation is absent):

  1. composer.json — check type. A *-bundle package from this vendor is conventionally a Symfony bundle, not a Laravel service provider. Note the autoload.psr-4 namespace; that's what you'll reference in config/app.php or your own provider.
  2. src/ — identify the entry point (a *Bundle, *ServiceProvider, or a manager/factory class) and the client/service classes it exposes.
  3. Any config/, Resources/config/, or DependencyInjection/ directory — this is where credentials and endpoint options are declared. If there's no publishable config, you'll be constructing the client manually.
  4. README / tests — tests are often the only accurate documentation of expected input/output shapes at this stage.

Registration. If the package ships a Laravel ServiceProvider, add it to bootstrap/providers.php (Laravel 11+) or config/app.php. If it only ships a Symfony Bundle class, you'll need a thin adapter provider:

// app/Providers/CainiaoPickupServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Tourze\CainiaoPickup\SomeClient::class, fn ($app) => new \Tourze\CainiaoPickup\SomeClient(
        config('services.cainiao.app_key'),
        config('services.cainiao.app_secret'),
    ));
}

First use case. Don't start with order creation. Start with a read-only call — a pickup-station/network lookup or a shipment query — executed in php artisan tinker with sandbox credentials. That proves auth, signing, and HTTP transport work before you write anything to a live system.

Implementation Patterns

Keep the vendor client behind your own boundary. The package API is 0.0.x and will move. Wrap it:

final class CainiaoPickupGateway
{
    public function __construct(private readonly SomeClient $client) {}

    public function stationsNear(string $city, string $district): array
    {
        $raw = $this->client->queryStations($city, $district);

        return collect($raw['data'] ?? [])
            ->map(fn (array $s) => new PickupStationDto(
                id: $s['stationCode'],
                name: $s['stationName'],
                address: $s['address'],
            ))
            ->all();
    }
}

Controllers and jobs depend on CainiaoPickupGateway, never on the package classes. When 0.0.2 renames something, you patch one file.

Config in config/services.php, secrets in .env.

'cainiao' => [
    'app_key'    => env('CAINIAO_APP_KEY'),
    'app_secret' => env('CAINIAO_APP_SECRET'),
    'sandbox'    => env('CAINIAO_SANDBOX', true),
],

Never let the package read env() directly — config caching will silently break it.

Async writes with idempotency. Pickup-order creation is a side-effecting external call: dispatch it on a queue job with $tries = 3, backoff() growing, and a client-supplied idempotency/business key derived from your own order UUID. Without that, a retry after a timeout produces duplicate pickup orders — the single most common production incident with this class of integration.

final class CreatePickupOrder implements ShouldQueue
{
    public int $tries = 3;
    public array $backoff = [10, 60, 300];

    public function handle(CainiaoPickupGateway $gateway): void
    {
        $gateway->createOrder($this->order, idempotencyKey: $this->order->uuid);
    }
}

Cache reference data aggressively. Station lists, district codes, and carrier lists change on the order of weeks. Cache::remember("cainiao.stations.{$city}", now()->addDay(), ...) cuts latency and — more importantly — keeps you off rate limits during traffic spikes.

Testing. Two layers:

  • Unit: bind a fake CainiaoPickupGateway implementation, or Http::fake() if the package uses Laravel's HTTP client (verify in src/ — if it uses Guzzle/Symfony HttpClient directly, Http::fake() won't intercept and you'll need to mock the client class).
  • Contract: a small, manually-run suite against the sandbox that asserts response shapes still parse. Run it before every dependency bump; it's your only early-warning system for breaking changes.

Roll out behind a flag. The package is pre-1.0. Put the pickup flow behind Feature::active('cainiao-pickup') with a manual fallback path.

Gotchas and Tips

0.0.1 means the public API is not yet stable. In semver terms every patch bump may break you. Constrain tightly ("tourze/cainiao-pickup-bundle": "0.0.1"), review the diff on each bump, and never composer update this package as part of a routine sweep. The empty release note is itself a signal: assume no changelog discipline and read the commit history on GitHub instead.

Symfony-vs-Laravel friction is the likeliest integration blocker. If it's a Symfony bundle, expect: Bundle class rather than ServiceProvider; Symfony Config component definitions instead of Laravel config arrays; constructor-injected services that need explicit container bindings; and no vendor:publish support. Budget time for a hand-written adapter provider — that's normal, not a red flag.

Business errors usually arrive with HTTP 200. Alibaba-family APIs commonly return transport success with an error code in the body (success: false, errorCode, subMsg). If you only ->throw() on status codes, failures will pass silently. Check the body code explicitly and throw your own domain exception.

Signature/auth failures are almost always one of three things: clock skew (sign the timestamp you actually send), character-encoding mismatch (UTF-8 vs GBK on address fields), or a secret with trailing whitespace from a copy-paste into .env. When you see "invalid signature", check these before touching the package.

Debugging: log the outbound request (with the signature and secret redacted) and the full raw response at debug level, in one structured entry keyed by your order UUID. Log::debug('cainiao.request', [...]) plus a catch that re-logs the raw body on parse failure will save you hours versus stack traces from deep inside the vendor code. If the package offers a debug/verbose option, enable it in local only.

Extension points to prefer over forking: bind an interface in your provider and decorate the client (log, retry, metrics) rather than editing vendor files. If a method is missing entirely, subclass or compose rather than patching — you'll need to upgrade off 0.0.1 eventually.

Sanitize before persisting. Raw gateway responses frequently embed API keys or session tokens. Strip them before writing to your activity_log, exception tracker, or queue payloads.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator