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

Filament Payment Manager Laravel Package

amidesfahani/filament-payment-manager

Filament Payment Manager is a Laravel/Filament package for administering payments from your admin panel. It provides Filament resources to view and manage payment records, monitor transaction statuses, and keep payment data organized in a familiar, ready-to-use interface.

View on GitHub
Deep Wiki
Context7

Getting Started

The package wires Shetabit/Payment into a Filament panel, so it assumes two things up front: a Filament panel provider and a Laravel app already comfortable with the underlying Shetabit driver model. Read the Shetabit docs before the plugin docs — the plugin is mostly a UI shell around Shetabit\Multipay\Invoice, Payment::config(), and Payment::callbackUrl().

Minimal setup:

composer require amidesfahani/filament-payment-manager
php artisan vendor:publish --tag="filament-payment-manager-config"
php artisan vendor:publish --tag="filament-payment-manager-migrations"
php artisan migrate

Then register the plugin on the panel you want it visible on. In app/Providers/Filament/AdminPanelProvider.php:

use Amidesfahani\FilamentPaymentManager\FilamentPaymentManagerPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(FilamentPaymentManagerPlugin::make());
}

First use case: log into /admin, open the created Payment Gateways (or similarly named) resource, and add a gateway record — driver name (zarinpal, idpay, zibal, payir, …), API key / merchant ID, sandbox toggle, and a callback URL. That row is what your frontend checkout should read from instead of hard-coding config/payment.php values.

Where to look first:

  1. config/filament-payment-manager.php — the driver list, model bindings, and table names.
  2. The published migration — check whether it stores credentials as plain columns or encrypted casts. This dictates whether you can safely query the model elsewhere.
  3. The plugin's Resources/ folder — that's the actual surface area; almost every customization is a Resource or Form override.

Implementation Patterns

Treat the plugin as an admin CRUD layer, not a payment processor. The package manages gateway configuration. The actual Payment::make($invoice, $callbackUrl, $driver) call still belongs in your own service or controller. A clean split:

// app/Services/CheckoutService.php
public function start(Order $order, Gateway $gateway): string
{
    $invoice = (new Invoice)->amount($order->total);
    $invoice->detail(['description' => "Order #{$order->id}", 'mobile' => $order->user->mobile]);

    return Payment::config($gateway->toShetabitConfig())
        ->callbackUrl(route('payments.callback', $order))
        ->purchase($invoice, function ($driver, $transactionId) use ($order) {
            $order->update(['transaction_id' => $transactionId]);
        })
        ->pay()
        ->render();
}

Add a toShetabitConfig() accessor on your gateway model (or use the plugin's model if it publishes one) that maps the stored columns to Shetabit's expected array — merchantId, sandbox, callbackUrl, etc. This keeps the admin UI decoupled from the runtime call.

Multi-panel strategy. If you have a tenant or vendor panel where merchants manage their own gateways, register the plugin there with a scoped query. Filament plugin classes rarely ship tenant scoping out of the box — override getEloquentQuery() in a subclassed Resource, or use a modifyQueryUsing on the plugin if supported:

FilamentPaymentManagerPlugin::make()
    ->usingModel(MerchantGateway::class)

Integrate with a payment/transaction ledger. The plugin is config-oriented; your transactions table should be separate and reference the gateway row by FK. Then hook the Shetabit callback (verify, paid events) into your own listener and update your ledger — don't expect the plugin to do this for you.

Form customization. Because Filament Resources are extensible, the day-to-day pattern is extend and override rather than fork:

class GatewayResource extends \Amidesfahani\FilamentPaymentManager\Resources\GatewayResource
{
    public static function form(Form $form): Form
    {
        return parent::form($form)->schema([
            ...parent::form($form)->getSchema(),
            TextInput::make('webhook_secret')->password()->revealable(),
        ]);
    }
}

Register the subclass via the plugin's ->resource() method if exposed, or by disabling the plugin's default resource and adding your own in the panel's ->resources([]).

Seed gateway rows via migrations/seeders in CI, not through the panel, for reproducible environments — the panel is for operator changes, seeders for baselines. Store credentials in .env and reference them in the seeder so production keys never live in a migration file.

Gotchas and Tips

  • Currency units. Shetabit's Invoice::amount() expects Rials, while many Iranian UIs display Toman (1 Toman = 10 Rials). The plugin won't enforce this. Pick one convention, write it down in a code comment near the form's amount field if the plugin exposes one, and stick to it — off-by-10x bugs here are common and expensive.
  • Callback URL must be publicly reachable and exact. Set it to route('payments.callback') and ensure APP_URL is the public domain. Shetabit's drivers are strict about trailing slashes on some (Zarinpal), lenient on others (IDPay). Prefer url()->route() without query strings.
  • Sandbox toggles are per-driver, not global. Each gateway row needs its own sandbox flag; verify each driver's config key (mode => 'sandbox' for Zarinpal vs sandbox => true for others). A shared "sandbox" checkbox in the UI that doesn't map to the driver's actual key is a silent failure mode.
  • Encryption of API keys. Check whether the model uses encrypted casts. If it doesn't, add them via a subclass. Filament's TextInput::password()->revealable() is a UI nicety, not encryption — anything stored plaintext in gateways.api_key is exposed to anyone with DB read access.
  • Filament v3 vs v4. The Filament plugin API changed meaningfully between v3 and v4 (Form::schema() vs Form::schema()->components(), ->plugin() signature, Section API). Pin to the Filament major the package was built against in composer.json ("filament/filament": "^3.2" typically) and don't bump casually.
  • Package maturity. 11 stars, single author, MIT. This is a convenience wrapper, not infrastructure. Read the migration and resource source before adopting — it's small enough to vendor (composer require then copy into app/Filament/) if you need to fork for driver support, and that's often the better path for a package this size.
  • Shetabit version drift. shetabit/payment occasionally changes driver config keys (notably zarinpal moving between merchantId and merchant_id, and idpay between api_key and key). If the plugin's form labels the field "Merchant ID" but Shetabit expects merchantId, verify in config/payment.php from the vendor folder — do not trust the label.
  • Missing driver = silent 500. Add a Select with ->options() backed by a static list, and validate in save() that the chosen driver exists under config('payment.drivers'). Otherwise the resource happily stores driver => 'zarinpall' and the failure surfaces at checkout, not at save time.
  • Debugging callbacks. Log::info('payment.callback', $request->all()) at the top of your callback route is the fastest way to tell whether the problem is your app or the gateway. Iranian gateways (especially Zibal and Pay.ir) get tetchy about GET vs POST on verify — check $request->method() in the log, not just the body.
  • Extension points to lean on: the plugin's ->navigationGroup(), ->navigationLabel(), ->navigationIcon() (or whatever fluent setters it exposes) are the low-friction knobs; model binding (->usingModel()) and resource replacement are the high-leverage ones. If a needed setter isn't there, subclass the plugin class rather than editing vendor/.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator