Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Payment Laravel Package

apility/payment

A Laravel/PHP payment package from Apility offering a shared interface for integrating payment gateways and processing transactions. It aims to simplify payment handling in PHP applications, though adoption is minimal and documentation is sparse.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish.

composer require apility/payment
php artisan vendor:publish --provider="Apility\Payment\PaymentServiceProvider"

The package is a Laravel service provider with auto-discovery, so there is no manual config/app.php registration on Laravel 9+. Start by opening config/payment.php after publishing — that file is the contract for everything else (gateway credentials, endpoint selection, callback route, currency).

Where to look first, in order:

  1. config/payment.php — driver names, sandbox flags, callback path.
  2. src/PaymentServiceProvider.php — confirms what is bound in the container and what is publishable.
  3. src/ gateway/driver directory — the concrete charge, authorize, capture, refund implementations.
  4. tests/ — with 0 stars and 0 dependents, the test suite is your only reliable spec. Read it before the README.

First use case: a redirect checkout.

// routes/web.php
Route::post('/checkout', [CheckoutController::class, 'start'])->name('checkout.start');

// controller
public function start(Request $request)
{
    $payment = Payment::driver('default')
        ->create([
            'amount'   => $order->total_minor_units, // integer
            'currency' => 'NOK',
            'reference' => (string) $order->id,
            'return_url' => route('checkout.return'),
            'callback_url' => route('payment.callback'),
        ]);

    return redirect($payment->redirectUrl());
}

Then a return route that reads status for UX and a callback route that is the source of truth. Do not grant access to goods from the return route alone.

Set expectations. This is a single-maintainer, low-adoption package (score 0.03, last release 2024-10-24). Treat the initial integration as a half-day of reading source, not a half-hour of reading docs. Budget for the possibility that you will need to fork or wrap it.

Implementation Patterns

Wrap the package, don't sprinkle it. Put all Payment:: calls behind one application-level PaymentGateway interface with start, capture, refund, status. This is the single highest-value pattern here: it isolates you from a package with no dependents and makes a future swap to laravel/cashier or a direct SDK a one-file change.

final class NetflexPaymentGateway implements PaymentGateway
{
    public function start(Order $order): RedirectResponse { /* Payment::driver(...) */ }
}

Callback/webhook as the authoritative state transition.

  • Register the callback route in routes/web.php but exclude it from CSRF (see Gotchas).
  • The handler should do exactly three things: verify authenticity, persist a PaymentEvent row keyed by the gateway's transaction id, then dispatch a domain event/queued job.
  • Make the handler idempotent by unique-indexing the gateway transaction id. Gateways routinely deliver the same callback twice.
  • Return 200 fast. Never do fulfilment, email, or PDF generation inside the callback request.

Model the payable side yourself. Rather than relying on a package HasPayments trait (if one exists, verify its migration assumptions), add payments and payment_events tables you control:

payments: id, payable_type, payable_id, driver, reference, amount, currency,
          status, gateway_id (unique), payload (json), timestamps

This gives you a queryable ledger and a debugging surface independent of the package's internals.

Fake it in tests. Locate whatever binding the service provider registers and swap it in a test service provider:

$this->app->bind(PaymentGateway::class, FakeGateway::class);

If the package offers no fake driver (likely), build one. Every test of your checkout flow should run without network calls.

Reconciliation job. Payment packages from small maintainers frequently miss edge cases (abandoned redirects, timeouts after the gateway charged). A scheduled php artisan payments:reconcile command that re-queries pending payments older than N minutes and asks the gateway for status is cheap insurance and, in practice, the piece that saves you.

Integration tips:

  • Keep amounts as integers in minor units end-to-end; cast at the edges only.
  • Store the raw gateway response as JSON on every transition. It is the only forensic trail you will have.
  • Dispatch PaymentSucceeded/PaymentFailed as queued events and fulfil in listeners, so retries are free.
  • If the package ships events, use them; if not, fire your own from your wrapper.

Gotchas and Tips

CSRF will silently kill your webhook. The gateway POSTs without a token. In Laravel 11+, add the callback path to bootstrap/app.php ->withMiddleware(fn ($m) => $m->validateCsrfTokens(except: ['payment/callback'])); on older versions add it to VerifyCsrfToken::$except. Test the route with curl against a live php artisan serve — this is the most common first-day failure.

Verify signatures, and fail closed. If config/payment.php has a secret/webhook signing key, make the handler reject unsigned requests with a 403 before touching the database. If the package does not verify signatures, add verification in your own middleware. Do not rely on the callback URL being secret.

Config caching bites during setup. php artisan config:clear after every edit to config/payment.php. If credentials are read via env() outside the config file (a common package bug), cached config will freeze them to null in production.

Sandbox and production may share keys or endpoints. Confirm whether the driver selects the endpoint from an APP_ENV check or a dedicated sandbox boolean. Never infer from APP_ENV — staging pointed at production is a classic incident.

Currency and rounding. Payment providers reject non-integer amounts and may enforce minimums per currency. Validate amount > 0 and a currency allowlist in a FormRequest before reaching the package; the package's own validation may be thin.

Auto-discovery and upgrades. The package is at 0 stars with no dependents, which means untested upgrade paths. Pin it in composer.json ("apility/payment": "~1.0" or a commit hash) and read the diff before every update. Run composer outdated deliberately.

Refunds and partial captures are where thin packages break. Test partial refund, full refund, double refund, and refund-after-failed-capture explicitly against the sandbox before launch. These paths are usually the least covered.

Debugging checklist, in order: (1) config cached? (2) CSRF? (3) route registered with the right HTTP verb and path? (4) are you looking at the queued job's failed jobs table (php artisan queue:failed)? (5) does the gateway's own dashboard show the transaction? The dashboard is the tiebreaker between "our code" and "their API."

Extension points to prefer: bind your own implementation of whatever the provider registers (facade root, manager, or gateway interface) rather than subclassing package classes. Subclassing couples you to internals that may change without a major version bump.

Logging hygiene. Payment payloads contain cardholder names, emails, and partial card data. Redact before logging, and never dump the full raw callback into laravel.log in production — that log gets shipped to third-party aggregators more often than anyone admits.

Have an exit plan. Because there is essentially no community around this package, write down what replacing it looks like (you already have the wrapper interface) and keep the raw gateway responses so that a migration is a data problem, not an archaeology problem.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin