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

V3 Php Sdk Laravel Package

quickbooks/v3-php-sdk

A PHP SDK for the QuickBooks Online API v3, offering OAuth authentication, CRUD operations, query support, and helpers for invoices, customers, payments, and other accounting entities. Simplifies integrating QuickBooks data into Laravel or plain PHP apps.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Release delta (v6.4.1) — why this changes the prior assessment. Three things landed that a TPM has to act on, none of which are feature work:

  1. PHP floor moved 7.2.5 → 8.2. Any environment on 7.2–8.1 cannot install this version at all. That is an upgrade prerequisite, not an SDK concern, and it may drag a Laravel upgrade with it depending on where the app sits.
  2. Guzzle floor moved ^7.9 → ^7.15.2. This is a correction to a material finding in the prior assessment: the SDK does carry a Guzzle dependency, which contradicts "transport is internal cURL with no PSR-18/Guzzle abstraction." Either the prior reading was wrong or transport moved to Guzzle between versions. This matters because it changes the test strategy story (Guzzle MockHandler / history middleware and record-replay become available; Http::fake() still does not apply because the SDK won't use Laravel's client). Treat as an open verification item for the spike, not a settled fact.
  3. Runtime security advisory (Oct 21, 2026): two critical RCEs, CVE-2026-109618 / -109619, in the PHP runtime — not in the SDK. Upgrading the SDK does not remediate them. The action is patching every web-facing PHP runtime to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 (or vendor patch).

Process signal — semver is not trustworthy here. Breaking changes shipped inside a patch-level release (6.4.1). Assume version numbers do not encode change magnitude; pin exact versions, read notes before every bump, and never use ^6.4 in production. That is a supply-chain hygiene cost, small but real and permanent.

Architecture fit. Unchanged in substance. Still a plain Composer library, not a Laravel package — no service provider, no publishable config, no facade, no DI bindings. It drops into a Laravel app the same way it drops into any PHP app; everything Laravel-native (config, queues, credential encryption, container mocking) remains our work. The SDK's real shape is generated entity classes mirroring the QBO XSD plus a DataService facade that owns auth, serialization, and transport in one object — it wants to own the request lifecycle end-to-end, which conflicts with Laravel idioms rather than complementing them. The Guzzle dependency partially softens the transport objection (test doubles become feasible at the Guzzle layer) but does not change the ownership model.

Coverage remains: Accounting API CRUD, the SQL-ish query() endpoint, reports, and CDC. It does not meaningfully cover webhook ingestion (Intuit's intuit-signature HMAC is ours to verify and route), and it has no retry/backoff, no rate-limit awareness, and no idempotency handling for requestid. Those three determine production stability and all three land in our codebase.

Integration feasibility. Still high in the trivial sense, moderate-to-hard in the real sense. What's new is a platform prerequisite in front of the integration work: we need PHP ≥ 8.2 on every app server, CI runner, and container image before this version is installable, and we need a Guzzle bump to ≥ 7.15.2 that is compatible with whatever the app (and Laravel's HTTP client) already pins. If the app is on PHP 8.1, this release is blocked until an infra upgrade lands — that is a dependency on another team's roadmap, not ours to schedule unilaterally.

The two original frictions stand: file-based token storage (file_oauth2_tokens, plus cookie-based sample flow) is not multi-tenant safe and must be replaced or bypassed; and no Laravel HTTP abstraction means test doubles are hand-rolled. The Guzzle finding improves the second point slightly — worth confirming early because it could remove a week of adapter scaffolding.

Technical risk.

Risk Severity Notes
Token rotation correctness High Access token ~60 min; refresh token rotates on every refresh and must persist atomically. Concurrent jobs on the same realm invalidate each other without a cache lock.
Default token persistence High File/cookie defaults are not multi-tenant safe and are easy to ship by accident.
No retry / 429 handling High ~500 req/min plus a daily cap per realm. Sync jobs own backoff, jitter, per-realm throttling.
Unpatched PHP runtime (RCE) High CVE-2026-109618 / -109619. Fixed by runtime patch, not by this SDK bump. Fleet-wide infra action with its own deadline.
PHP 8.2 floor blocks install High (schedule) Hard prerequisite. Couples the integration to an infra/Laravel upgrade timeline we may not control.
Version-number integrity Medium Breaking changes in a patch release. Pin exact versions; review notes on every bump.
Transport assumption now uncertain Medium Guzzle dependency contradicts prior "internal cURL" finding. Verify in spike; affects test strategy and any existing Guzzle pin conflicts.
Vendor maintenance cadence Low–Medium Downgraded. A security-driven release is evidence the vendor responds; the thin community surface (287 stars, 0 dependents) still means we are largely self-supporting on regressions.
Generated code / PHP version drift Medium XSD-generated classes accumulate deprecation noise on new PHP minors. Now more acute: pinning to the 8.2 floor means we sit on a security-only branch.
Coupling to SDK object model Medium Entities leak into domain code if unguarded.
Sandbox/production divergence Medium "Passed in sandbox" remains weak evidence.

Key questions (revised).

  1. What is the PHP runtime baseline across app servers, CI, and container images, and who owns patching it? This now gates the upgrade and carries an RCE advisory with it.
  2. Which Laravel/PHP pairing are we on, and does the 8.2 floor force a framework upgrade? (Laravel 10 supports PHP 8.1–8.3; Laravel 11+ requires 8.2+. Staying on L10 means pinning PHP 8.3, not 8.2, since 8.2 is security-fixes-only.)
  3. Does our app or another dependency pin guzzlehttp/guzzle below 7.15.2? Resolve before scheduling the bump.
  4. Is the requirement full bidirectional sync, or QBO-as-system-of-record with one-way push? (3x scope difference.)
  5. Which entities at launch — and are attachments, journal entries, or payments in scope? All materially harder than Invoice/Customer.
  6. Multi-realm tenancy: one QBO company per tenant or shared? Realm count at 12 months drives throttling design.
  7. Who owns the Intuit developer account, app registration, and production OAuth review?
  8. Tolerance for eventual consistency in financial data, and is a reconciliation UI needed?
  9. Is there existing middleware (MuleSoft, Boomi, warehouse) already talking to QBO?

Integration Approach

Two tracks, deliberately separated. The v6.4.1 release splits this work into (a) a security/infra workstream that must happen regardless of whether we adopt the SDK, and (b) the integration workstream that depends on it. Do not let (a) hide inside (b)'s estimate, and do not let (b) start before (a) clears the environment.

Track A — platform remediation (start now, owner: infrastructure).

  1. Patch PHP runtimes fleet-wide to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 per the advisory. This is orthogonal to the SDK and should not wait on it.
  2. Raise the app's PHP floor to ≥ 8.2 (recommend 8.3 or 8.4 if staying on Laravel 10) across CI matrix, Dockerfiles, and local dev.
  3. Audit and bump Guzzle to ≥ 7.15.2; check transitive pins from Laravel's HTTP client and any other SDKs.
  4. Pin exact SDK version in composer.json — no caret ranges, given the patch-level breaking change.

Track B — integration (unchanged in shape, now with a different first step).

Stack fit. Wrap it behind our own interface on day one:

  • App\Integrations\QuickBooks\Client exposing only the operations we need (createInvoice, upsertCustomer, queryCustomers, getChangesSince) — not the SDK's ~60-method surface.
  • SdkQuickBooksClient as the sole file importing QuickBooksOnline\API\*.
  • FakeQuickBooksClient for tests and local dev.

Token and credential model. Do not use the SDK's file store. quickbooks_connections table (tenant_id, realm_id, encrypted access_token, encrypted refresh_token, access_expires_at, refresh_expires_at, status, minor_version) uniquely keyed on realm_id, with Cache::lock("qbo:refresh:{$realmId}") around refresh. Laravel's encrypted cast for at-rest encryption. On each job: load, refresh within a 5-minute skew window, persist access + rotated refresh token in one transaction, then construct DataService with explicit tokens.

Configuration. config/quickbooks.php; client id/secret from env; baseUrl switched on app()->environment(); minorversion pinned explicitly, never floating.

Sequencing.

  1. Platform gate (Track A). PHP runtime patched, 8.2+ in CI and images, Guzzle ≥ 7.15.2, SDK pinned exact. Hard gate — nothing downstream starts until green.
  2. Spike (1 week). Sandbox app, OAuth connect, one entity create + query, confirm token rotation under concurrency, and confirm transport layer (Guzzle vs cURL) to settle the test-double question. Kill criteria if token handling proves unworkable without forking.
  3. Foundation. Adapter interface, connection model, encrypted storage, refresh lock, config, per-tenant feature flag.
  4. Read path. Pull-only sync for 1–2 launch entities behind a queued job — real rate-limit behavior learned without write risk.
  5. Write path. Idempotent creates via requestid, plus a qbo_entity_map table (tenant, local_id, qbo_id, qbo_sync_token) to make retries safe.
  6. Incremental sync. CDC endpoint + scheduled job replacing full pull.
  7. Webhooks. Signature-verification controller dispatching into the existing sync jobs — an optimization on top of a working poll, never the sole mechanism.
  8. Hardening. Backoff, dead-letter queue, reconciliation report, reconnect-on-disconnect admin UI.

Webhooks stay last: a poll-based sync that works beats a webhook-driven one that silently drops deliveries when the queue backs up.

Compatibility. Confirm the PHP matrix against every runtime before committing, and treat the 8.2 floor as the minimum, not the target — 8.2 is security-fixes-only, so plan the next bump rather than settling there. Verify ext-curl/ext-openssl/CA bundle in the container image. Reconcile Guzzle constraints against the app and against Laravel's own HTTP client pin; a conflicting pin here is the most likely cause of a failed composer update.

Operational Impact

Maintenance. The SDK's steady-state cost is unchanged and modest if wrapped — roughly 0.1 FTE for the adapter and ~0.5 FTE for the sync/reconciliation layer in year one. What v6.4.1 adds is a new recurring obligation with a security SLA attached: PHP runtime patching across the fleet. That is not an integration cost, but this integration is what makes it unavoidable, and it will recur on the PHP release cadence indefinitely. Budget for it explicitly and name an owner; unowned runtime patching is how a "we upgraded the SDK" ticket turns into an incident.

Expect: one SDK review per year plus this kind of security-driven bump, a PHP-version test each cycle, and standing attention to Intuit's API changelog (minorversion deprecations carry roughly a year of runway). Also budget for release-note review before every bump, because patch versions can break installs here.

Support. Intuit's developer support is forums, docs, and paid partner tiers; the GitHub repo is not a reliable triage channel at this star count. The v6.4.1 security response is reassuring about vendor diligence, but internal support still concentrates in one or two people who understand QBO error semantics — a bus-factor risk worth naming. Mitigate with a runbook and an error-code mapping table. Add a second, short runbook for runtime patching and SDK pin review, so that obligation isn't tribal knowledge either.

Scaling. Three axes, unchanged by this release:

  • Per-realm limits (~500 req/min, daily cap): throughput scales with tenant count, but per-tenant throughput needs explicit throttling via a per-realm Redis token bucket so one noisy tenant can't starve others.
  • Batch endpoints (up to 30 ops/call) are the main bulk-push lever — design batching in from the start, since retrofitting it means rewriting job granularity.
  • Query paging (~1000 rows/page, no joins) makes full pulls slow by construction; CDC is the answer, subject to its own window and delete-limitation caveats that need validating on real tenant data.

Failure modes to instrument from day one.

Failure Symptom Mitigation
Refresh token race Both jobs get invalid_grant; connection dies Cache lock; single-flight refresh
Refresh expiry / user revoke invalid_grant on connect; realm goes silent Mark needs_reauth, surface in admin UI, notify tenant
429 / daily cap Intermittent failures clustering by tenant Per-realm token bucket, exponential backoff with jitter, alert on sustained 429s
400 validation Silent per-record failures inside a batch Parse batch responses per-record; never treat HTTP 200 as full success
Stale sync token 400 Stale Object Error on update Re-fetch, re-merge, retry once, then dead-letter
Partial batch failure Data drift with no alert Nightly reconciliation diffing counts/balances
Webhook signature mismatch Dropped events, sync lag Verify HMAC; alert on mismatch rate; never disable verification to "fix" it
Unpatched PHP runtime RCE on web-facing servers; discovered by an attacker, not by us Fleet patch to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35; track CVE advisories independently of the SDK; verify via scanner, not via composer.lock
Broken-pin install composer update fails or silently downgrades on a patch bump Exact version pins; lockfile review; dependabot-style PRs with release-note review, not auto-merge

Ramp-up. A competent Laravel engineer reaches a working sandbox call in under a day; productive ownership of the sync engine takes 3–4 weeks (OAuth lifecycle, QBO error taxonomy, entity relationships, accounting semantics). The accounting-domain learning is routinely underestimated — staff someone who has looked at an invoice/journal-entry data model, or make a domain expert available for review. Add a smaller, separate ramp for the infra side: whoever owns PHP patching needs to be in the room before the SDK bump is scheduled, not after it fails.

Overall TPM read. v6.4.1 does not change the architectural verdict — the package is still a reasonable, low-risk dependency if treated as a thin transport and serialization layer behind our own adapter. What it changes is the critical path and the risk register. The install prerequisite (PHP ≥ 8.2) and the runtime RCE advisory turn this from a self-contained engineering task into a cross-team one: infra must patch PHP and bump Guzzle before integration can adopt the version, and that patching obligation is permanent. Two corrections to carry forward: Guzzle is a real dependency, so the test-double story may be better than previously assessed (verify in spike); and version numbers here do not signal change magnitude, so pin exact and review every release.

Recommend proceeding, on three conditions: (1) Track A — runtime patch plus PHP/Guzzle bumps — is resourced and scheduled as its own deliverable with a named owner, not folded into integration estimates; (2) the one-week spike confirms token handling under concurrency and settles the transport question; (3) the Intuit developer-account and app-review workstream runs in parallel with engineering rather than trailing it. The opportunity score is unchanged: QBO is the default accounting system for US SMBs, so this is usually a hard product requirement rather than a nice-to-have — but the SDK solves request construction only. Tenancy, token rotation, throttling, idempotency, reconciliation, app review, and now runtime hygiene are ours, and they remain the bulk of the estimate.

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.
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
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums