1. Go to this page and download the library: Download atannex/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/ */
atannex / billing example snippets
// An MTN number goes to MTN MoMo, an Orange number to Orange Money. Nothing else to choose.
$payment = Billing::amount(5000)->phone('677 12 34 56')->for($order)->charge();
$payment->status; // PaymentStatus::Processing: the payer has a prompt on their phone
// A USD card payment goes to Stripe Checkout.
$payment = Billing::amount('49.90', 'USD')->method('card')->returnTo(route('orders.show', $order))->charge();
return redirect($payment->redirectUrl());
use Atannex\Billing\Facades\Billing;
$payment = Billing::amount(5000) // major units, default currency (XAF)
->for($order) // what is paid for (morph)
->by($user) // who pays; Billable models fill name/email/phone
->phone('677 12 34 56') // any common format; drives operator routing
->description('Annual subscription')
->returnTo(route('orders.show', $order), route('cart')) // after hosted pages
->idempotencyKey("order-{$order->id}")
->metadata(['plan' => 'annual'])
->charge();
use Atannex\Billing\Concerns\Billable; // on User
use Atannex\Billing\Concerns\Payable; // on Order, Invoice, Ticket…
$user->charge(5000)->for($order)->charge();
$user->payments()->paid()->get();
$order->pay(5000)->phone($phone)->charge();
$order->isPaid(); // a successful payment, not fully refunded
$order->amountPaid('XAF'); // Money, net of refunds
$order->latestPayment;
$payment->money(); // Money (5000 XAF)
$payment->refundable(); // what can still be refunded
$payment->net(); // kept after refunds
$payment->status; // PaymentStatus enum
$payment->isOpen(); $payment->isPaid(); $payment->succeeded(); $payment->failed();
$payment->failure_code; // approval_rejected, not_enough_funds, expired, amount_mismatch, …
$payment->failure_message; // readable: "The payer declined the payment."
$payment->events; // the audit ledger
Payment::open()->get(); Payment::paid()->gateway('mtn_momo')->get(); Payment::status('failed')->get();
Billing::find('PAY-01J…');
Billing::refresh($payment); // ask the provider now
Billing::cancel($payment); // open payments only; expires the Stripe session / cancels the intent
Billing::refund($payment); // everything that is left
Billing::refund($payment, 2000, 'Damaged item'); // part, in major units of the payment's currency
Billing::refund($payment, Money::of('9.90', 'EUR'));
Event::listen(PaymentSucceeded::class, function (PaymentSucceeded $event) {
$event->payment->payable?->markPaid();
});
use Atannex\Billing\Facades\Billing;
use Atannex\Billing\Enums\PaymentStatus;
$fake = Billing::fake()->process(); // succeed() · fail('code', 'message') · process() · atest('id')->first();
$fake->settle($payment, PaymentStatus::Succeeded); // the provider confirms, through the real processor
$this->assertTrue($order->fresh()->isPaid());
$fake->assertChargedTimes(1); $fake->assertNothingCharged(); $fake->assertRefunded();
use Atannex\Billing\Gateways\AbstractGateway;
class CinetPayGateway extends AbstractGateway
{
protected array $capabilities = [Capability::Redirect, Capability::Webhooks];
public function initiate(Payment $payment): Snapshot { /* call the API, return a Snapshot */ }
public function fetch(Payment $payment): Snapshot { /* map the provider's status */ }
public function verifyWebhook(Request $request, ?Payment $payment): bool { /* … */ }
public function parseWebhook(Request $request): WebhookEvent { /* … refresh: true */ }
}
Billing::extend('cinetpay', fn (string $name, array $config, $app) => new CinetPayGateway(
$name, $config, $app->make(\Illuminate\Http\Client\Factory::class), $app['cache']->store(),
));