承接 aldiazhar/laravel-invoice 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

aldiazhar/laravel-invoice

Composer 安装命令:

composer require aldiazhar/laravel-invoice

包简介

A flexible and powerful invoice management package for Laravel

README 文档

README

A flexible and powerful invoice management package for Laravel applications.

Requirements

  • PHP 8.1, 8.2, or 8.3
  • Laravel 10.x, 11.x, or 12.x

Installation

composer require aldiazhar/laravel-invoice

Publish Assets

php artisan vendor:publish --tag=invoice-config
php artisan vendor:publish --tag=invoice-migrations
php artisan migrate

Quick Start Guide

Two Ways to Create Invoice

Method 1: From Payer (User) Perspective

$invoice = $user->invoice()
    ->pay($payment)     // What to pay
    ->create();

Method 2: From Invoiceable (Payment) Perspective

$invoice = $payment->bill()
    ->to($user)         // Bill to whom
    ->create();

Both methods create the same invoice!

1. Setup Models

User Model (Payer):

use Aldiazhar\Invoice\Contracts\Payer;
use Aldiazhar\Invoice\Traits\HasInvoices;

class User extends Model implements Payer
{
    use HasInvoices;

    public function getPayerName(): string
    {
        return $this->name;
    }

    public function getPayerEmail(): ?string
    {
        return $this->email;
    }

    public function getPayerAddress(): ?string
    {
        return $this->address;
    }

    public function getPayerMetadata(): array
    {
        return ['phone' => $this->phone];
    }
}

Payment Model (Invoiceable):

use Aldiazhar\Invoice\Contracts\Invoiceable;
use Aldiazhar\Invoice\Traits\Invoiceable as InvoiceableTrait;

class Payment extends Model implements Invoiceable
{
    use InvoiceableTrait;

    public function getInvoiceableDescription(): string
    {
        return "Payment #{$this->id}";
    }

    public function getInvoiceableAmount(): float
    {
        return $this->amount;
    }

    public function getInvoiceableMetadata(): array
    {
        return [
            'payment_method' => $this->payment_method,
            'reference' => $this->reference_number,
        ];
    }

    public function onInvoicePaid($invoice): void
    {
        $this->update(['status' => 'completed']);
    }
}

2. Create Invoice

Auto Item (Recommended - No items needed!):

// From Payer perspective
$invoice = $user->invoice()
    ->pay($payment)
    ->create();

// From Invoiceable perspective  
$invoice = $payment->bill()
    ->to($user)
    ->create();

Item otomatis dibuat dari getInvoiceableDescription() dan getInvoiceableAmount()

Manual Single Item:

$invoice = $user->invoice()
    ->pay($payment)
    ->item('Payment Processing', 100000)
    ->create();

Multiple Items:

$invoice = $user->invoice()
    ->pay($order)
    ->item('Product A', 50000, 2)
    ->item('Product B', 75000, 1)
    ->tax(10000)
    ->discount(5000)
    ->create();

Using Array:

$invoice = $user->invoice()
    ->pay($order)
    ->items([
        ['name' => 'Product A', 'price' => 50000, 'quantity' => 2, 'tax_rate' => 0.11],
        ['name' => 'Product B', 'price' => 75000, 'quantity' => 1],
    ])
    ->create();

Without Auto Item (Manual Control):

$invoice = $user->invoice()
    ->pay($order)
    ->withoutAutoItem()
    ->item('Custom Item', 100000)
    ->create();

Advanced:

$invoice = $user->invoice()
    ->pay($order)
    ->item('Premium Package', 500000)
    ->tax(20000)
    ->discount(50000)
    ->currency('USD')
    ->due('2024-12-31')
    ->description('Monthly subscription')
    ->meta(['campaign_id' => 123])
    ->after(function($invoice) {
        // Custom logic after create
    })
    ->onPaid(function($invoice) {
        // Custom logic when paid
    })
    ->create();

3. Invoice Management

$invoice->markAsPaid();
$invoice->cancel();
$invoice->markAsFailed();
$invoice->refund();

if ($invoice->isPaid()) {
    //
}

if ($invoice->isOverdue()) {
    //
}

4. Partial Payments

$invoice->addPayment(50000, 'bank_transfer', [
    'reference' => 'TRX-123',
    'notes' => 'First installment',
]);

$paidAmount = $invoice->getPaidAmount();
$remaining = $invoice->getRemainingAmount();
$isFullyPaid = $invoice->isFullyPaid();

5. Recurring Invoices

$invoice = $user->invoice()
    ->pay($subscription)
    ->item('Monthly Premium', 99000)
    ->makeRecurring('monthly', now()->addYear())
    ->create();

$schedule->command('invoices:generate-recurring')->daily();

6. Query & Statistics

use Aldiazhar\Invoice\Facades\Invoice;

$pending = Invoice::pending();
$paid = Invoice::paid();
$overdue = Invoice::overdue();

$stats = Invoice::stats();

$userStats = $user->getInvoiceStats();

7. Activity Log

$activities = $invoice->activities;

foreach ($activities as $activity) {
    echo $activity->action;
    echo $activity->description;
    echo $activity->causer->name;
}

Auto Item Feature

Default Behavior (Auto Item Enabled):

$payment = Payment::create(['amount' => 100000]);

$invoice = $user->invoice()
    ->pay($payment)
    ->create();

Secara otomatis membuat item dengan:

  • Name: dari getInvoiceableDescription()
  • Price: dari getInvoiceableAmount()
  • Quantity: 1

Disable Auto Item:

$invoice = $user->invoice()
    ->pay($payment)
    ->withoutAutoItem()
    ->item('Custom Item', 50000)
    ->item('Another Item', 50000)
    ->create();

Force Add Invoiceable Item:

$invoice = $user->invoice()
    ->pay($payment)
    ->item('Extra Service', 20000)
    ->withInvoiceableItem()
    ->create();

Total: 120000 (100000 from payment + 20000 from extra)

Configuration

Edit config/invoice.php:

return [
    'currency' => 'USD',
    'due_date_days' => 30,
    'strict_validation' => true,
    
    'invoice_number' => [
        'prefix' => 'INV-',
        'format' => 'Ymd',
        'padding' => 4,
    ],
];

Validation

Strict Mode (Default)

Invoice total must match invoiceable amount:

$payment = Payment::create(['amount' => 100000]);

$invoice = $user->invoice()
    ->pay($payment)
    ->create();

Total: 100000

$invoice = $user->invoice()
    ->pay($payment)
    ->item('Custom', 50000)
    ->create();

Error: Mismatch! Expected 100000, got 50000

Disable Strict Mode

$invoice = $user->invoice()
    ->pay($payment)
    ->item('Custom', 50000)
    ->withoutStrictValidation()
    ->create();

Total: 50000

API Reference

InvoiceBuilder Methods

// Set who pays
->to($payer)         // Set payer (who will pay)

// Set what to pay
->pay($invoiceable)  // Set invoiceable (what to pay)

// Items
->item($name, $price, $quantity = 1, $taxRate = 0)
->items(array $items)
->withInvoiceableItem()
->withoutAutoItem()

// Amounts
->tax(float $amount)
->discount(float $amount)

// Details
->currency(string $currency)
->status(string $status)
->due($date)
->description(string $description)
->meta(array $metadata)

// Options
->withoutStrictValidation()
->makeRecurring($frequency, $endDate, $interval)

// Callbacks
->after(callable $callback)
->onPaid(callable $callback)

// Execute
->create()

Invoice Methods

$invoice->markAsPaid()
$invoice->cancel()
$invoice->markAsFailed()
$invoice->refund()
$invoice->addPayment($amount, $method, $data)
$invoice->generateNextInvoice()
$invoice->isPaid()
$invoice->isPending()
$invoice->isOverdue()
$invoice->getPaidAmount()
$invoice->getRemainingAmount()
$invoice->isFullyPaid()

Query Scopes

Invoice::pending()
Invoice::paid()
Invoice::failed()
Invoice::overdue()
Invoice::forPayer($payer)
Invoice::forInvoiceable($invoiceable)

Examples

Simple Payment Invoice (Auto Item)

$payment = Payment::create([
    'user_id' => $user->id,
    'amount' => 150000,
    'payment_method' => 'bank_transfer',
]);

$invoice = $user->invoice()
    ->pay($payment)
    ->create();

Item dibuat otomatis dari payment!

E-Commerce Order (Manual Items)

$invoice = $user->invoice()
    ->pay($order)
    ->withoutAutoItem()
    ->items($order->items->map(fn($item) => [
        'name' => $item->product->name,
        'price' => $item->price,
        'quantity' => $item->quantity,
        'sku' => $item->product->sku,
    ])->toArray())
    ->tax($order->tax_amount)
    ->discount($order->discount_amount)
    ->create();

Subscription (Recurring + Auto Item)

$subscription = Subscription::create([
    'plan' => 'premium',
    'price' => 99000,
]);

$invoice = $user->invoice()
    ->pay($subscription)
    ->makeRecurring('monthly')
    ->onPaid(function($invoice) {
        $invoice->invoiceable->renew();
    })
    ->create();

Service Payment (Mixed Items)

$service = Service::create([
    'name' => 'Consulting',
    'base_amount' => 1200,
]);

$invoice = $user->invoice()
    ->pay($service)
    ->withInvoiceableItem()
    ->item('Travel Cost', 200)
    ->item('Materials', 100)
    ->tax(0.11)
    ->create();

Total: 1500 + 11% tax

Troubleshooting

Interface Implementation Issue

Jika muncul error "must implement Invoiceable interface", pastikan:

use Aldiazhar\Invoice\Contracts\Invoiceable;
use Aldiazhar\Invoice\Traits\InvoiceableTrait;

class Payment extends Model implements Invoiceable
{
    use InvoiceableTrait;
    
    public function getInvoiceableDescription(): string
    {
        return "Payment #{$this->id}";
    }

    public function getInvoiceableAmount(): float
    {
        return $this->amount;
    }
}

Jangan lupa implement semua method yang required!

Using bill()->to() vs invoice()->pay()

// These are EQUIVALENT:

// Option 1: From Payer
$user->invoice()->pay($payment)->create();

// Option 2: From Invoiceable  
$payment->bill()->to($user)->create();

// Both create the same invoice!

Amount Mismatch Error

$payment = Payment::create(['amount' => 100000]);

$invoice = $user->invoice()
    ->pay($payment)
    ->item('Custom', 50000)
    ->withoutStrictValidation()
    ->create();

Atau pastikan total items = invoiceable amount

License

MIT License

Support

统计信息

  • 总下载量: 14
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-11-15