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/ */
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\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);
}
}
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'));
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.