PHP code example of gusmanwidodo / laravel-billing

1. Go to this page and download the library: Download gusmanwidodo/laravel-billing library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

gusmanwidodo / laravel-billing example snippets


use Gusmanwidodo\Billing\Facades\Billing;

$invoice = Billing::newInvoice('USD')
    ->addItem('Pro plan', unitPrice: 5000, quantity: 2, taxRate: 0.10) // $50.00 x2, 10% tax
    ->addItem('Setup fee', unitPrice: 1500, discount: 500)             // $15.00 - $5.00
    ->discount(1000)      // invoice-level discount: $10.00
    ->dueInDays(14)
    ->create();

$invoice->total;                 // integer minor units, e.g. 11000
$invoice->total()->format();     // "110.00 USD"

use Gusmanwidodo\Billing\Support\Money;

Money::fromMajor(50.00, 'USD')->amount;   // 5000
Money::of(5000, 'USD')->format();          // "50.00 USD"

use Gusmanwidodo\Billing\Concerns\HasInvoices;

class Organization extends Model
{
    use HasInvoices;
}

$org->invoice('USD')
    ->addItem('Enterprise seat', 100000, quantity: 5)
    ->create();

$org->invoices;         // all invoices billed to this org

// Record a payment (your gateway/cash/transfer — the package is agnostic):
Billing::recordPayment($invoice, 4000, attributes: [
    'method' => 'transfer',
    'reference' => 'TRX-123',
]);

$invoice->refresh();
$invoice->status;                 // 'partially_paid'
$invoice->amountDue()->format();  // remaining balance

Billing::recordPayment($invoice, 7000);
$invoice->refresh();
$invoice->status;                 // 'paid' (amount_paid >= total)
$invoice->paid_at;                // set automatically

Billing::void($invoice);   // status = 'void'; further payments are rejected

use Gusmanwidodo\Billing\Support\InvoiceStatus;

InvoiceStatus::DRAFT;           // 'draft'
InvoiceStatus::SENT;            // 'sent'
InvoiceStatus::PARTIALLY_PAID;  // 'partially_paid'
InvoiceStatus::PAID;            // 'paid'
InvoiceStatus::VOID;            // 'void'

'default_currency' => 'USD',
'number_prefix'    => 'INV-',   // auto invoice numbers: INV-20260101-AB12CD
'decimals'         => 2,

use Gusmanwidodo\Billing\Facades\Billing;

// 1. Open a charge -> returns a pending PaymentIntent (with redirect URL if the
//    provider uses hosted checkout).
$intent = Billing::charge($invoice, provider: 'stripe');
$redirect = $intent->meta['redirect_url'] ?? null;

// 2. The provider calls your webhook. Verify + settle in one call:
$intent = Billing::handleWebhook('stripe', $request->getContent(), $request->headers->all());
// On a verified success event, a real Payment is recorded and the invoice
// becomes 'paid' automatically. Signature verification is mandatory — a forged
// or tampered webhook throws and records nothing.

// 3. Refund a succeeded intent:
Billing::refund($intent);            // full
Billing::refund($intent, 2000);      // partial (minor units)

$intent = Billing::charge($invoice, provider: 'manual');
// ...later, after confirming a bank transfer:
Billing::confirmIntent($intent, ['reference' => 'TRANSFER-123']);

use Gusmanwidodo\Billing\Payments\{PaymentProvider, ChargeResult, WebhookEvent, RefundResult};
use Gusmanwidodo\Billing\Support\PaymentIntentStatus;

class StripeProvider implements PaymentProvider
{
    public function name(): string { return 'stripe'; }

    public function createCharge($invoice, int $amount, string $currency, array $options = []): ChargeResult
    {
        // ...call Stripe, create a PaymentIntent/Checkout Session...
        return new ChargeResult(externalId: $session->id, status: PaymentIntentStatus::PENDING, redirectUrl: $session->url);
    }

    public function verifyWebhook(string $payload, array $headers): bool
    {
        // MUST verify the signature. Return false on any mismatch.
        return \Stripe\Webhook::constructEvent($payload, $headers['Stripe-Signature'] ?? '', $secret) !== null;
    }

    public function parseWebhook(string $payload, array $headers): WebhookEvent
    {
        $event = json_decode($payload, true);
        return new WebhookEvent(
            type: $event['type'],
            externalId: $event['data']['object']['id'],
            status: PaymentIntentStatus::SUCCEEDED, // map from the event type
            amount: $event['data']['object']['amount'],
            currency: strtoupper($event['data']['object']['currency']),
        );
    }

    public function refund($intent, ?int $amount = null): RefundResult
    {
        // ...call Stripe refund...
        return new RefundResult(externalId: $refund->id, amount: $amount ?? $intent->amount);
    }
}

app(\Gusmanwidodo\Billing\Payments\PaymentProviderManager::class)
    ->register(new StripeProvider(), asDefault: true);

use Gusmanwidodo\Billing\Facades\Subscriptions;
use Gusmanwidodo\Billing\Models\Plan;

// Define a plan
$plan = Plan::create([
    'key' => 'pro-monthly', 'name' => 'Pro Monthly',
    'amount' => 5000, 'currency' => 'USD',      // $50.00 / cycle (minor units)
    'interval' => 'month', 'interval_count' => 1,
    'trial_days' => 14,                          // optional trial
]);

// Subscribe any billable
$sub = Subscriptions::subscribe($user, $plan);
// or with the trait:
$user->subscribeTo($plan);
$user->subscribed();          // true
$user->activeSubscription();  // the Subscription

// app/Console — run hourly/daily
use Gusmanwidodo\Billing\Facades\Subscriptions;

$invoices = Subscriptions::generateDueInvoices();
// For each subscription whose next_billing_at has arrived: creates an invoice
// (status 'sent') and advances the cycle. Trialing subs activate on first bill.

// Laravel scheduler (bootstrap/app.php or a Kernel)
$schedule->call(fn () => \Gusmanwidodo\Billing\Facades\Subscriptions::generateDueInvoices())
    ->hourly();

Subscriptions::pause($sub);                          // stop billing while paused
Subscriptions::resume($sub);                         // reschedule next bill = now
Subscriptions::cancel($sub, immediately: false);     // cancel at period end (grace)
Subscriptions::cancel($sub, immediately: true);      // end access now

use Gusmanwidodo\Billing\Facades\Billing;

$invoice = Billing::newInvoice('USD')->addItem('Plan', 5000)->create();

$html  = $invoice->toHtml();                    // always available
$bytes = $invoice->toPdf();                     // needs dompdf; raw PDF bytes
return $invoice->downloadPdf('invoice.pdf');    // download response

// Or resolve the services directly:
app(\Gusmanwidodo\Billing\Pdf\InvoicePdf::class)->save($invoice, storage_path('inv.pdf'));

'pdf' => [
    'view' => 'billing::invoice',
    'paper' => 'a4',
    'orientation' => 'portrait',
    'vendor' => ['name' => 'Acme Inc.', 'address' => "123 Main St\nCity"],
],

use Gusmanwidodo\Billing\Facades\Subscriptions;
use Gusmanwidodo\Billing\Models\Plan;
use Gusmanwidodo\Billing\Support\PlanType;

$plan = Plan::create([
    'key' => 'api-calls', 'name' => 'API calls', 'type' => PlanType::METERED,
    'amount' => 100,            // $1.00 per unit (minor units)
    'currency' => 'USD', 'interval' => 'month', 'interval_count' => 1,
]);

$sub = $user->subscribeTo($plan);

// Record usage during the cycle
Subscriptions::recordUsage($sub, 30);
Subscriptions::recordUsage($sub, 20);
Subscriptions::unbilledUsage($sub);   // 50

// On the cycle's due date, generateDueInvoices() bills 50 * 100 = 5000 and
// resets the counter (records are marked billed).
Subscriptions::generateDueInvoices();

$invoice = Subscriptions::swapPlan($sub, $proPlan);
// Creates a single proration invoice:
//   new plan prorated for the remaining days
//   minus a credit for the unused days of the old plan
// then moves the subscription onto the new plan.
bash
composer migrate
# optionally:
php artisan vendor:publish --tag=billing-config
php artisan vendor:publish --tag=billing-migrations
bash
php artisan vendor:publish --tag=billing-views
# edits resources/views/vendor/billing/invoice.blade.php