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

Invoicable Laravel Package

bkfdev/invoicable

Invoicable adds flexible invoicing to Laravel apps, letting you generate invoices from Eloquent models with line items, taxes, totals, and status tracking. A lightweight, developer-friendly package for billing workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install/update the package:
    composer require <vendor>/<package>:^1.1
    
  2. Publish and review configuration and migrations:
    php artisan vendor:publish --tag=<package>-config
    php artisan vendor:publish --tag=<package>-migrations
    
  3. Run migrations, paying attention to the new store_id column added to payments in v1.1.8:
    php artisan migrate
    
  4. Look first at:
    • config/<package>.php for default store/tenant behavior and model bindings.
    • The published payments migration for the store_id foreign key, nullability, and indexing.
    • The package Payment model for $fillable, relationships, and scopes.
    • Your application Store model for the inverse payments() relationship.
  5. First use case: create a payment associated with a store and read it back through the relationship:
    $payment = $store->payments()->create([
        'amount' => 2500,
        'currency' => 'USD',
        // other package-required fields
    ]);
    
    $store->payments()->latest()->get();
    

Implementation Patterns

Assigning store_id consistently

Since v1.1.8, payments carry a store_id. Treat it as required application context, even if the database column is nullable for backward compatibility.

$payment = Payment::create([
    'store_id' => $store->id,
    'amount' => $amount,
    'currency' => $currency,
]);

Prefer creating through the relationship where possible:

$store->payments()->create($payload);

Store-scoped queries

Use the relationship or explicit scoping for all store-facing reads:

$payments = $store->payments()
    ->where('status', 'succeeded')
    ->latest()
    ->paginate();

For dashboards or reporting, group by store:

Payment::query()
    ->selectRaw('store_id, SUM(amount) as total')
    ->groupBy('store_id')
    ->with('store')
    ->get();

Request validation and authorization

Validate that the submitted store_id belongs to the authenticated user/tenant, not just that it exists:

$data = $request->validate([
    'store_id' => ['required', 'integer', 'exists:stores,id'],
    'amount' => ['required', 'integer', 'min:1'],
]);

abort_unless(
    $request->user()->stores()->whereKey($data['store_id'])->exists(),
    403
);

If your application uses a current-store context, resolve store_id server-side rather than trusting request input.

Webhooks, jobs, and imports

Every payment creation path must set store_id. Audit:

  • Webhook handlers that create or update payments.
  • Queued jobs that replay payment events.
  • CSV/API imports.
  • Admin panel or Nova/Filament resources.
  • Factories and seeders.
Payment::factory()->create([
    'store_id' => Store::factory(),
]);

API resources and serialization

Expose store_id deliberately. If clients only need the store relationship, consider exposing a nested identifier instead:

return [
    'id' => $this->id,
    'store_id' => $this->store_id,
    'amount' => $this->amount,
    'currency' => $this->currency,
];

Eager load store when list endpoints include store details to avoid N+1 queries.

Backfilling existing payments

For existing installations, migrate in stages:

  1. Add nullable store_id.
  2. Backfill from the owning order/customer/account relation.
  3. Add the foreign key and index.
  4. Update application code to always write store_id.
  5. Optionally make the column non-nullable once all rows are populated.

Keep the backfill idempotent and batched.

Gotchas and Tips

  • Nullable vs required store_id: v1.1.8 may add the column as nullable to avoid breaking existing rows. Do not assume it is always populated. Add application-level validation and a backfill plan.
  • Missing writes: The most common failure after this release is a payment creation path that omits store_id, leaving orphaned or unscoped payments. Search the codebase for Payment::create, payments()->create, updateOrCreate, and raw inserts.
  • Authorization leaks: Filtering by store_id only in the UI is insufficient. Enforce store ownership in policies, form requests, or query scopes.
  • Foreign key behavior: Check whether the migration uses cascadeOnDelete, restrictOnDelete, or no constraint. Deleting a store should not silently delete financial records unless that is explicitly intended.
  • Mass assignment: Confirm store_id is in the package model’s $fillable or assign it explicitly. If the package guards it, use forceFill() carefully or the relationship.
  • N+1 queries: Listing payments with store names will trigger a query per row unless you with('store').
  • Testing isolation: Add feature tests with two stores and assert that a user/store context cannot read or mutate the other store’s payments.
  • Config quirks: If the package supports a default store or tenant resolver, verify it is not overriding explicitly supplied store_id values unexpectedly.
  • Debugging: Use php artisan migrate:status, inspect the payments table schema, log the raw SQL via DB::listen(), and verify $payment->store resolves correctly.
  • Extension points: Prefer model events/observers, custom casts, or a subclassed Payment model bound in config over editing vendor migrations. For multi-tenant apps, a global scope keyed to the current store may be appropriate, but ensure background jobs explicitly set the store context.
  • Upgrade check: After upgrading to v1.1.8, run the full test suite, then exercise payment creation, listing, reporting, and webhook flows in a staging environment with multiple stores.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib