bazzly / payoffice
Laravel package to use all fintech payment API and fetch server for pings
Package info
Type:package
pkg:composer/bazzly/payoffice
Requires
- php: ^7.2|^8.0|^8.1|^8.2|^8.3|^8.4
- guzzlehttp/guzzle: ~6|~7|~8|~9
- illuminate/support: ~6|~7|~8|~9|^10.0|^11.0|^12.0|^13.0
- peppeocchi/php-cron-scheduler: ^4.0
Requires (Dev)
- mockery/mockery: ^1.3
- orchestra/testbench: ^11.0
- phpunit/phpunit: ^11.5.50|^12.5.8|^13.0
This package is auto-updated.
Last update: 2026-08-02 13:01:12 UTC
README
All your payment providers in one place.
Payoffice is a Laravel package that solves one specific problem: a payment provider's API can go down or slow down, and your checkout shouldn't blindly trust it. Before sending a customer to a payment gateway, Payoffice can ping the provider's API host and report whether it's up, down, and how fast it's responding — so your app can pick a healthy provider or warn the user instead of failing mid-checkout.
On top of that, it bundles ready-to-use integrations for popular fintech APIs so you don't have to wire each one up from scratch.
Table of contents
- Features
- Requirements
- Installation
- Configuration
- Usage
- Testing
- Local simulation
- Roadmap / known limitations
- Credits
- License
Features
- Provider health check — ping a payment API's host over TCP and get back status
(
up/down) and latency in milliseconds before deciding whether to use it. - Paystack payments — initialize transactions, verify payments, manage plans, customers, subscriptions, payment pages, and subaccounts.
- Paystack transfers — look up banks, resolve account numbers, create transfer recipients, and send money.
- Secure transaction references — cryptographically random reference/token generator.
- Config-driven, multi-provider — Paystack, Flutterwave, Interswitch, and Remita are pre-scaffolded in the published config file.
Requirements
| Requirement | Version |
|---|---|
| PHP | ^7.2 | ^8.0 | ^8.1 | ^8.2 | ^8.3 | ^8.4 |
Laravel (illuminate/support) |
^6 | ^7 | ^8 | ^9 | ^10 | ^11 | ^12 | ^13 |
guzzlehttp/guzzle |
^6 | ^7 | ^8 | ^9 |
Local development (running the test suite / examples/demo.php) is verified against the
latest supported combination — PHP 8.4, Laravel 13, PHPUnit 13, orchestra/testbench 11 —
via require-dev, regardless of which older Laravel/PHP version you build against.
Installation
Install via Composer:
composer require bazzly/payoffice
Laravel's package auto-discovery will register Bazzly\Payoffice\PaymentServiceProvider
automatically — no manual entry in config/app.php is needed.
Publish the config file and the pings_monitoring migration:
php artisan vendor:publish --provider="Bazzly\Payoffice\PaymentServiceProvider" --tag="config" php artisan vendor:publish --provider="Bazzly\Payoffice\PaymentServiceProvider" --tag="pings_monitoring" php artisan migrate
Publishing the config is required — the package reads provider settings from
config/payoffice.phponce it exists in your app, not from the package's internal copy.
Configuration
The published config/payoffice.php is a list of provider entries. Fill in credentials via
your .env file:
# Paystack PAYSTACK_PUBLIC_KEY= PAYSTACK_SECRET_KEY= PAYSTACK_PAYMENT_URL=https://api.paystack.co PAYSTACK_MERCHANT_EMAIL= PAYSTACK_MERCHANT_CURRENCY=NGN # Flutterwave FLW_PUBLIC_KEY= FLW_SECRET_KEY= FLW_SECRET_HASH= # Interswitch INTERSWITCH_GATEWAY_TYPE=WEBPAY INTERSWITCH_CURRENCY=566 INTERSWITCH_SITE_REDIRECT_URL= INTERSWITCH_ENV=TEST INTERSWITCH_SPLIT=false INTERSWITCH_COLLEGE= INTERSWITCH_SEND_MAIL=false INTERSWITCH_MAC_KEY= INTERSWITCH_PRODUCT_ID= INTERSWITCH_PAY_ITEM_ID= # Remita MERCHANTID= SERVICETYPEID= FUNDINGACCOUNT= FUNDINGBANKCODE= APIKEY= MANDATETPYE=DD
Each provider entry in config/payoffice.php has a name and APIURL key used by the
health-ping feature, plus a provider-specific block (paystack, flutterwave, etc.) used
by that provider's integration classes.
Usage
Server / API health ping
PingServer checks whether a payment API host is reachable and how fast it responds, so you
can gate checkout on the provider actually being up.
use Bazzly\Payoffice\PingServer; // PingServer(string $name, string $url, ?int $preferredPingMs = null) $check = new PingServer('paystack', 'api.paystack.co', 100); $result = $check->getUrlServerDetails(); // $result = [ // 'name' => 'paystack', // 'apiurl' => 'api.paystack.co', // 'serverStatus'=> 'up', // PingServer::UPSTATUS / PingServer::DOWNSTATUS // 'serverPing' => 42, // measured latency in ms // 'userPing' => 100, // your preferred/acceptable threshold (defaults to 10) // ]; if ($result['serverStatus'] === PingServer::UPSTATUS && $result['serverPing'] <= $result['userPing']) { // safe to redirect the customer to this provider } else { // provider is down or too slow — fall back to another provider }
To check every provider configured in config/payoffice.php in one pass:
use Bazzly\Payoffice\PingServer; $providers = config('payoffice'); $statuses = collect($providers)->map(function ($provider) { $ping = new PingServer($provider['name'], $provider['APIURL']); return $ping->getUrlServerDetails(); });
Persisting ping results
If you want a history of ping checks (e.g. for an internal status dashboard), the
pings_monitoring migration and PingsMonitoring Eloquent model are included:
use Bazzly\Payoffice\Models\PingsMonitoring; use Bazzly\Payoffice\PingServer; $result = (new PingServer('paystack', 'api.paystack.co', 100))->getUrlServerDetails(); PingsMonitoring::create([ 'name' => $result['name'], 'apiurl' => $result['apiurl'], 'serverStatus' => $result['serverStatus'], 'serverPing' => $result['serverPing'], 'userPing' => $result['userPing'], ]);
This is a manual step — the package does not currently schedule ping checks for you. Wire it into your own
app/Console/Kernel.phpschedule if you want periodic checks.
Paystack — payments
GetPaid wraps the Paystack transactions/plans/customers/subscriptions/pages/subaccounts
API. Most methods read their payload straight from the current request (i.e. they're meant
to be called from a controller handling a form POST), but you can also pass an explicit
$data array to bypass that.
use Bazzly\Payoffice\Paystack\GetPaid; $paystack = new GetPaid(); // Initialize a transaction and redirect the customer to Paystack public function pay(GetPaid $paystack) { return $paystack->getAuthorizationUrl()->redirectNow(); } // Or supply the payload explicitly instead of relying on request() input $paystack->getAuthorizationUrl([ 'amount' => 500000, // kobo 'email' => 'customer@example.com', 'reference' => $paystack->genTranxRef(), ]); // Verify a transaction after the customer returns from Paystack public function callback(GetPaid $paystack) { if ($paystack->isTransactionVerificationValid()) { $payment = $paystack->getPaymentData(); // mark the order as paid } }
Other available methods:
| Method | Purpose |
|---|---|
genTranxRef() |
Generate a unique transaction reference |
getAllCustomers() / createCustomer() / fetchCustomer($id) / updateCustomer($id) |
Customer management |
getAllPlans() / createPlan() / fetchPlan($code) / updatePlan($code) |
Subscription plan management |
createSubscription() / getAllSubscriptions() / getCustomerSubscriptions($id) / getPlanSubscriptions($id) / enableSubscription() / disableSubscription() / fetchSubscription($id) |
Subscription management |
createPage() / getAllPages() / fetchPage($id) / updatePage($id) |
Paystack payment pages |
createSubAccount() / fetchSubAccount($code) / listSubAccounts($perPage, $page) / updateSubAccount($code) |
Split-payment subaccounts |
exportTransactions() / getAllTransactions() |
Transaction reporting |
Paystack — transfers & banks
Transfer handles paying money out — bank lookups, recipient creation, and transfers.
use Bazzly\Payoffice\Paystack\Transfer; $transfer = new Transfer(); // List supported banks $banks = $transfer->getBanks('nigeria'); // Confirm an account number belongs to the expected owner $account = $transfer->confirmAccount('0123456789', '058'); // Send money to a bank account $transfer->sendMonyToAccDetails( accName: 'Jane Doe', accNumber: '0123456789', bankName: 'Guaranty Trust Bank', metadata: [ 'bankCode' => '058', 'source' => 'balance', 'amount' => 500000, // kobo 'reference'=> $transfer->getBanks() ? uniqid('trf_') : null, 'reason' => 'Vendor payout', ] ); // Check your Paystack balance $balance = $transfer->getBalance(); // Verify a transfer by its reference $status = $transfer->getVerifyTransfer('trf_abc123');
Transaction reference generator
TransRef generates cryptographically-random reference strings/tokens (useful for anything
needing a unique, non-guessable identifier — not limited to Paystack).
use Bazzly\Payoffice\TransRef; $reference = TransRef::getHashedToken(); // 25-char alphanumeric token by default $reference = TransRef::getHashedToken(12); // custom length
Testing
composer install vendor/bin/phpunit
PingServer, GetPaid, and Transfer all have automated coverage. The Paystack tests
don't hit the real API — they inject a Guzzle MockHandler
via an optional constructor argument (new GetPaid($handlerStack) /
new Transfer($handlerStack)), so the suite runs offline with no credentials needed:
use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; $mock = new MockHandler([ new Response(200, [], json_encode(['status' => true, 'data' => [/* ... */]])), ]); $paystack = new GetPaid(HandlerStack::create($mock));
Omit the argument in real usage (new GetPaid()) and it talks to the real Paystack API
as normal.
Tests use PHPUnit's #[Test] attribute (PHPUnit\Framework\Attributes\Test), not the
older /** @test */ docblock — the latter was removed in PHPUnit 11+. Match that
convention for any new test you add.
Local simulation
examples/demo.php is a runnable, no-credentials-needed walkthrough of the whole
package — a real network health check via PingServer, plus a mocked Paystack
payment and payout flow via GetPaid/Transfer (same MockHandler approach as the
tests). Useful for seeing the package work end-to-end without setting up a Laravel app
or real API keys:
composer install php examples/demo.php
Roadmap / known limitations
This package is under active development. Known rough edges before you rely on it in production:
- Scheduled/periodic pinging and automatic persistence to
PingsMonitoringare not wired up yet; you must trigger and store checks yourself. - The Paystack integration is a fork of
unicodeveloper/laravel-paystackadapted to this package's config structure — behavior should match the upstream package, but hasn't yet been independently re-verified end-to-end here.
Credits
- ALLI BAZEET — author
- unicodeveloper — the Paystack
integration in
src/Paystackis adapted fromlaravel-paystack; enormous credit for the original, well-documented implementation this was built on.
License
The MIT License (MIT). See LICENSE for details.