jeffersongoncalves/laravel-woocommerce
A Laravel package that provides a simple wrapper for the WooCommerce REST API, letting you manage products, orders, customers, and other store data from your PHP application with a fluent, framework-friendly interface.
It's a REST client, not an ORM — no Eloquent models, no local mirror. Everything is a synchronous HTTP call to your store, so design around that from day one.
composer require the package, then php artisan vendor:publish to get the config file..env: store URL, consumer key, consumer secret, and the REST namespace (pin it — wc/v3 is not guaranteed on older or heavily plugin-modified stores).tinker: fetch system status or a single product by ID, then list recent orders. If that works, auth and TLS are correct and everything else is just resource plumbing.Look first at: the config file (it tells you what's overridable), the facade's resource methods, and the bundled Laravel Boost guidelines — for an HTTP client like this, the Boost skill file is effectively a better cheatsheet than the README, and it's the fastest way to get correct method names and payload shapes into an AI-assisted workflow.
First real use case: a read-only order dashboard — pull the last 50 orders, render totals and statuses. It exercises auth, pagination, currency parsing and date handling in one go, and breaks nothing in production.
Fluent resource calls. Expect Woocommerce::products()->all() / find() / create() / update() / delete(), with nested resources under a parent (->variations($productId), ->notes($orderId), ->refunds($orderId)). Verify the exact signatures against the README or Boost guidelines before writing wrappers around them — at 1.0.0 the surface is stable enough to use but worth confirming once.
Paginate explicitly, never blindly. Ask for per_page => 100 and loop pages until you get a short page; read X-WP-TotalPages from the response if the package exposes it. An all() on a 40k-order store is 400+ sequential requests and will either time out or get you throttled.
Cache the read-heavy catalogue. Products, categories and tags change rarely: Cache::remember keyed by resource + query hash, 15–60 minute TTL. Skip the cache entirely for stock, price and order status — stale stock causes oversells.
Push-sync jobs, not request-time writes. Queue anything that writes. Resolve products by sku before creating (WooCommerce will happily create duplicates), use updateOrCreate-style logic, and give each job retry plus WithoutOverlapping so a slow store doesn't stack up parallel writers.
Batch for bulk, in chunks of 100. batch() is the right tool for importing 500 products, but it is not transactional — the response contains per-operation results and individual items can fail while the request returns 200. Always iterate and log the failures. Only collections with a /batch endpoint support it; reports and system status do not.
Receive webhooks yourself. The package manages webhook registrations; it does not ship an inbound receiver. Build the controller, verify the X-WC-Webhook-Signature HMAC-SHA256 against the raw body, respond 200 fast, and dispatch a job that fetches the order/product by ID — treat the webhook as a notification, not as the payload of record.
Wrap errors once. Catch Illuminate\Http\Client\RequestException, read code from the JSON body (woocommerce_rest_authentication_error, woocommerce_rest_invalid_product_id, …) and map it to your own exceptions. Retry 429/5xx with backoff; never retry a 4xx — it's a validation or auth problem and the retry just burns quota.
Test with Http::fake(). Because it's built on Laravel's HTTP client, you can assert the outgoing method, URL and JSON body, and replay captured real responses as fixtures. This is the single biggest maintainability win over hand-rolled cURL clients.
date_created is store-local time with no offset; date_created_gmt is UTC. Persist the _gmt fields and convert for display. The after/before filters are ISO8601 interpreted in site timezone, not UTC.bcmath or integer minor units, and treat total as authoritative over summing line items.reassign with a WordPress user ID to take ownership of their posts. Deleting without it fails.stock_quantity on a variable product silently does nothing useful. This is the most common inventory bug with this API.meta_data is asymmetric. Read gives {id, key, value}; write wants [{key, value}], and writing to protected keys is silently ignored.status=any to see pending/failed alongside completed..env, run config:cache again — and note the facade resolves one client, so per-request store switching (multi-store setups) needs you to override config and clear the resolved facade instance, or instantiate the client directly. Multi-store is the main architectural limitation to plan around.code is globally unique. Creating a duplicate throws rather than merging; check first if you're syncing from an external system.X-WP-Total/X-WP-TotalPages alongside each call, and use Telescope or a Http::globalRequestMiddleware to capture the actual URL — namespace and query-encoding mistakes (category wants an ID, not a slug) look like empty results, not errors.How can I help you explore Laravel packages today?