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

Moneybird Bundle Laravel Package

ruudk/moneybird-bundle

Symfony bundle for integrating the Moneybird accounting platform into your app. Provides configuration and service wiring for the Moneybird API, making it easier to work with invoices, contacts, and financial data.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the package is archived and effectively unusable for new work. Last release 2015-08-04, Symfony 2.x-era, and it targets the retired Moneybird v1 API (the current API is v2, which changed auth scopes, URL structure — every resource is now nested under /administrations/{id}/... — and payload shapes). The only realistic reason to read this bundle is to maintain an existing Symfony 2.8/3.x app that already talks to a legacy Moneybird account, or to crib the OAuth wiring pattern.

For a new integration, depend on an actively maintained PHP client (picqer/moneybird-php-client is the de-facto one) and write ~50 lines of Symfony service wiring yourself. You will spend less time than resurrecting this bundle.

If you genuinely must install it:

  1. composer require ruudk/moneybird-bundle (expect resolver conflicts on anything above Symfony 3.x / PHP 7.x).
  2. Register the bundle in app/AppKernel.php (new Ruudk\MoneybirdBundle\MoneybirdBundle()).
  3. Publish config to see the real keys: app/console config:dump-reference moneybird. The parameters are OAuth client credentials plus a redirect route — check DependencyInjection/Configuration.php for the exact tree rather than trusting README examples, which predate several Symfony config-schema changes.
  4. Look at Resources/config/services.xml first. The bundle is thin: it registers a Moneybird client service plus whatever OAuth plumbing it needs. That file tells you every service ID you can inject and every place you can override.
  5. Complete the OAuth handshake once (authorize URL → redirect → exchange code for a token), persist the token, then call the client service from a command or controller.

The first real use case is almost always: pull the administrations list, then pull contacts or sales invoices for one administration and reconcile them locally.

Implementation Patterns

Token bootstrap as a one-shot console command. The bundle doesn't give you a durable token store. The pattern that works: a moneybird:authorize command that prints the authorize URL, a controller action at the registered redirect route that receives ?code=, exchanges it via the client, and writes the token to a Doctrine entity or a file. Everything else in your app then reads that stored token. Do not try to re-run the flow per request.

Inject the client, not the bundle. Keep your domain code dependent on the bundle's client service only at the composition root. Wrap it in your own MoneybirdGateway interface with methods like administrations(), contacts($administrationId), salesInvoices($administrationId, $filters). When you eventually migrate to API v2, you rewrite one class instead of grepping the codebase.

Pull-based sync over scheduled commands. There were no useful webhooks in the v1 era. Run a moneybird:sync command from cron, keep a last_synced_at column, and use it as a filter. Make the command idempotent by upserting on Moneybird's numeric ID, not on name or email.

Map money to integer cents immediately. API payloads carry amounts as strings/decimals. Convert at the boundary into an integer-cents value object and never let a float touch an invoice total. Same for tax rates — store the rate and the base separately, never a pre-computed total, or your reconciliation will drift by a cent on rounding.

Isolate pagination in the gateway. Moneybird pages results; v1 used page/per_page query params. Put the loop inside your gateway so callers get a generator or a full collection, and add a hard page cap so a misbehaving API can't spin forever.

Test by faking the gateway, not the HTTP layer. Because the bundle's client is a concrete service, the cheapest test strategy is to bind your MoneybirdGateway interface to a fixture-backed fake in config/services_test.yml. Asserting against recorded HTTP fixtures of a dead API has poor value.

Decorator for logging. Register a decorator on the bundle's client service that logs method, URL, status, and body at debug level. This is the single highest-value addition for debugging, and it survives the eventual client swap.

Gotchas and Tips

  • Version wall. The bundle predates Symfony 4 autowiring, the services.yaml defaults, and PHP 7.2+ return-type strictness. Expect to patch services.xml for public/private service visibility and to add public: true on anything you fetch from the container in a legacy app. On PHP 8 it will likely fatal on deprecations-turned-errors or old each()-style code in transitive deps.
  • The API behind it is gone. Moneybird v1 was sunset; endpoints return errors rather than data. No amount of bundle fixing makes it work against a current Moneybird account. Verify this before scoping any work — it is the difference between "a day of patching" and "a rewrite".
  • Administration scoping. v1 tokens were user-scoped and you selected an administration per call; v2 bakes the administration ID into the path and into token scope. Any code that assumes a global client with no administration context will need restructuring, not just a base-URL change.
  • Redirect URI must match byte-for-byte, including trailing slash and scheme, against what's registered in the Moneybird app settings. A mismatch produces an opaque OAuth error page, not a helpful message. Check this first when auth "just fails".
  • Secrets in parameters.yml. Old Symfony convention encourages committing credentials. Move client_id/client_secret to environment variables from day one; you'll be rotating them during the migration anyway.
  • Silent token expiry. If the stored token is rejected you'll get a generic 401 that looks like a auth misconfiguration. Log the raw response body and status before your gateway throws, and distinguish 401 (token) from 403 (scope) from 404 (wrong administration or dead v1 route).
  • Rate limiting and retries. The API throttles. Add exponential backoff on 429 in the decorator, and jitter your cron so a fleet of workers doesn't synchronize on the same minute.
  • Extension points are shallow. You can override service definitions and replace the client class via the container, but there's no event system, no pluggable serializer, no repository abstraction. Don't plan an architecture that assumes you can hook into the bundle's internals — assume you own everything above the HTTP client.
  • Don't fork it. With 3 stars, 0 dependents, and a decade of rot, a fork is a private maintenance burden with no upstream. Extract the ~100 lines of OAuth plumbing you actually need and delete the dependency.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package