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

Pinduoduo Api Bundle Laravel Package

tourze/pinduoduo-api-bundle

A Symfony bundle that integrates the Pinduoduo open-platform API, wiring configuration and services through dependency injection so PHP apps can call PDD e-commerce endpoints with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

0.0.1 is the initial tag. The release notes contain only an auto-generated Full Changelog link, so there is no feature list, migration note, or BC promise to read — treat this as "first public snapshot" and evaluate the code, not the changelog.

  1. composer require tourze/pinduoduo-api-bundle:0.0.1 — pin the exact version, this is pre-1.0.
  2. Check the package type before anything else. Tourze publishes Symfony bundles: expect a *Bundle class, DependencyInjection/*Extension.php, and YAML/XML service wiring — not a Laravel ServiceProvider. There will be no package auto-discovery, no php artisan vendor:publish, and no config/pinduoduo.php merge. Read composer.json (type, extra.symfony, autoload.psr-4) first.
  3. Decide your integration depth. Two viable paths:
    • Thin: ignore the bundle scaffolding and consume only the client/API classes directly (autoloaded PSR-4, so they work fine in Laravel). This is the recommended path for most apps.
    • Adapter: write a small PinduoduoServiceProvider that binds the client as a singleton and mirrors the bundle's expected config keys into config/services.php + .env.
  4. Where to look in the source: the service definition files (they reveal the constructor signature — client_id, client_secret, access_token, base URL, HTTP client), the base client class (signing + transport), and the per-namespace API classes (Goods, Order, Logistics, AfterSales, auth/token).
  5. First call: wire credentials from a Pinduoduo Open Platform test app, then hit the cheapest read-only method — pdd.time.get (connectivity/sign check) followed by pdd.goods.cats.get (real payload shape). If both succeed you have config, signing, and deserialization proven in one shot.

Implementation Patterns

Config as a singleton, tokens as data. The client config carries client_id/client_secret (app-level, static) but access_token is per-shop and expires. Do not bake the token into a singleton. Bind an app-level client and build a short-lived per-shop client from it:

// config/services.php
'pinduoduo' => [
    'client_id'     => env('PDD_CLIENT_ID'),
    'client_secret' => env('PDD_CLIENT_SECRET'),
],

// app/Services/Pinduoduo/ClientFactory.php
public function forShop(Shop $shop): PddClient
{
    return new PddClient(
        config('services.pinduoduo.client_id'),
        config('services.pinduoduo.client_secret'),
        $shop->pdd_access_token,
    );
}

Wrap the bundle, don't sprinkle it. Create App\Services\Pinduoduo\{OrderService,GoodsService,LogisticsService} that own the API classes. When the bundle inevitably changes a method name at 0.0.2, you change one adapter instead of 40 call sites.

Token lifecycle is your job. PDD access tokens expire (order of a month) and there is a refresh endpoint (pdd.pop.auth.token.refresh). Store access_token, refresh_token, expires_at per shop; refresh in a scheduled command and lazily on a token-invalid error code. A single shared global token is the number one way this breaks in multi-shop apps.

Queue outbound writes. Any *.add / *.update / *.send call goes through a job with RateLimited/redis throttle and exponential backoff ([1, 5, 15, 60]), because PDD enforces per-app QPS and returns errors rather than blocking. Read-heavy calls can also be queued and cached.

Message push (webhook) receiver. PDD pushes order/logistics/refund events to an endpoint. Pattern: verify signature → persist raw payload keyed by msg_id (unique index) → return 200 immediately (< a few seconds or it retries) → dispatch a job to process. Never do the downstream work in the HTTP handler.

Testing. Because the transport is almost certainly Guzzle/PSR-18, stub it with a MockHandler (or Http::fake() if the bundle happens to use the Laravel client) and assert on the signed parameter array — that's where your real bugs live.

Gotchas and Tips

  • 0.0.1 = no stability contract. Pin 0.0.1 exactly; a ^0.0.1 constraint would still allow 0.0.2 breaking changes since Composer treats 0.0.x as fully unstable. Read the diff before any bump.
  • Symfony bundle in a Laravel app. No auto-discovery, no publishable config, no facades. If you use the *Bundle class you must instantiate and register the container extension yourself — usually not worth it. Budget an hour for the adapter, or skip it.
  • Signature errors are ~always one of four things: unsorted params, wrong sign algorithm/casing (PDD's is MD5(secret + k1v1k2v2... + secret), uppercase hex), params dropped because the value was null/'', or a non-string being passed (ints must be cast). If the bundle signs for you, do not pre-sign — double-signing produces the same opaque 10001-style error.
  • HTTP 200 does not mean success. PDD returns failures as {"error_response": {"error_code": ..., "error_msg": ...}} with a 200 status. Your transport layer must throw on error_response presence, not on status code. Otherwise failures silently become null return values.
  • Timestamps are Asia/Shanghai and formatted Y-m-d H:i:s, not Unix epochs. Set the timezone on the formatter explicitly, not on the app global — this bites during DST-free-but-offset confusion in CI.
  • Never log the secret or a signed payload — the signature plus params can leak. Log the method name, shop id, and error_code only. Truncate access_token to 6 chars.
  • Business vs transport errors need distinct handling. Transport (timeout, TLS, 5xx) → retry. Business (error_code for insufficient stock, invalid state) → do not retry, surface to the domain layer.
  • Extension point for unsupported methods: find the base client's generic request(string $method, array $params) (usually public or protected). Subclass it for methods the bundle hasn't generated yet — that gives you full API coverage without waiting for the package, which matters a lot at 0.0.1.
  • Debug move: capture the outgoing signed parameter array in a local-only log channel for one request. 90% of integration time is lost guessing at the sign.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource