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

Gateway Laravel Package

parsisolution/gateway

Parsisolution Gateway is a Laravel package offering a unified interface for multiple payment gateways. It simplifies configuration, payment initiation, and callback verification, letting PHP applications integrate online payments through a consistent, reusable API.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require parsisolution/gateway
    

    Laravel’s package discovery registers the service provider automatically. If you’re on an older Laravel version, add the provider and any facade alias manually in config/app.php.

  2. Publish the configuration

    php artisan vendor:publish --provider="Parsisolution\Gateway\GatewayServiceProvider"
    

    Check the generated config/gateway.php (name may differ slightly) first — it is the source of truth for:

    • supported drivers (Zarinpal, IDPay, Pay.ir, Zibal, Mellat, Saman, Parsian, etc.)
    • the default driver key
    • merchant / terminal / API key fields per gateway
    • callback URL and sandbox flags
  3. Set environment variables

    GATEWAY_DRIVER=zarinpal
    GATEWAY_MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    GATEWAY_CALLBACK_URL="${APP_URL}/payment/callback"
    GATEWAY_SANDBOX=true
    

    Keep the sandbox on until you have a full round-trip working against the gateway’s test environment.

  4. Where to look first

    • The package README for the exact gateway keys and facade name — driver naming varies (e.g. zarinpal vs zarinpal_rest).
    • config/gateway.php for the credential shape each driver expects.
    • The package’s src/Drivers/ directory to confirm which methods each driver actually implements (some support refund/inquiry, many do not).
  5. First use case — a single purchase round-trip

    // 1. Create a payment and get a redirect URL
    $payment = Gateway::driver('zarinpal')->request($amountRial, $description);
    return redirect($payment->getRedirectUrl());
    
    // 2. In the callback route, verify using the gateway’s reference
    $result = Gateway::driver('zarinpal')->verify($request->input('Authority'), $amountRial);
    

    Get this working end-to-end in sandbox before adding a second driver.

Implementation Patterns

Wrap the package in your own service. Don’t call the package facade directly from controllers. Introduce an App\Services\PaymentService (or PaymentGateway interface) that maps your domain’s Order to the package’s request/verify calls. This gives you one place to swap drivers, add logging, and mock in tests.

Two-phase flow is mandatory for Iranian gateways. Almost every gateway follows request → redirect → callback → verify:

// Phase 1: persist before redirecting
$tx = PaymentTransaction::create([
    'order_id' => $order->id,
    'gateway'  => $driver,
    'amount'   => $order->total, // store the exact unit sent to gateway
    'status'   => 'pending',
    'authority'=> null,
]);
$payment = Gateway::driver($driver)->request($order->total, $order->description);
$tx->update(['authority' => $payment->getAuthority()]);
return redirect($payment->getRedirectUrl());

// Phase 2: verify in callback
$tx = PaymentTransaction::where('authority', $request->input('Authority'))->firstOrFail();
$result = Gateway::driver($tx->gateway)->verify($tx->authority, $tx->amount);
if ($result->isSuccessful()) {
    $tx->update(['status' => 'paid', 'ref_id' => $result->getReferenceId()]);
    $order->markPaid();
}

Store the gateway key on the transaction, not just the config. A user may start with Zarinpal and you may later switch the default driver to Zibal. The callback must verify with the same driver that created the request.

Use a transaction state machine. States like pending → redirected → verified | failed | cancelled make reconciliation and admin debugging far easier than a single paid boolean. Iranian gateways frequently deliver a callback but the user closes the browser before the redirect; you need pending payments you can reconcile later.

Multi-gateway selection per order. Let the user pick a gateway on checkout, then pass the driver explicitly:

Gateway::driver($order->preferred_gateway)->request(...);

Persist the choice. Keep a fallback driver if the primary gateway is down.

Callback route hygiene. Register a single callback route that dispatches on the stored transaction, not on a query-string gateway parameter:

Route::match(['get','post'], '/payment/callback', PaymentCallbackController::class)
     ->name('payment.callback');

Use match because different gateways return GET vs POST.

Testing strategy. Wrap the package behind your PaymentGateway interface and bind a fake in tests that returns a canned success/failure. Reserve real package calls for an integration test suite hitting the sandbox. Never let CI hit production gateways.

Logging pattern. Log the raw request and response payloads (minus secrets) around every request() and verify() call. Iranian gateway error messages are often opaque Persian strings; you’ll need the raw response when reconciling.

Gotchas and Tips

  • Rial vs Toman is the number-one source of bugs. Most drivers expect Rials; store and send one canonical unit, convert at the edge, and assert the amount you verify matches the amount you requested. A mismatch will fail verification or, worse, undercharge.
  • Verify server-side against your stored amount, never against $request->input('amount'). Attackers can tamper with callback parameters.
  • Verify can succeed once and fail on retry. Gateways often reject a second verify with an “already verified” error. Make your verify handler idempotent: if the transaction is already paid, return success without calling the gateway again.
  • Callbacks arrive with different reference formats. Zarinpal sends Authority, IDPay sends id + order_id, Pay.ir sends trans_id + factorNumber. Normalize immediately in one controller and look up your transaction by the field that driver uses.
  • Sandbox flags are per-driver, not global. GATEWAY_SANDBOX=true may not affect every driver; check each driver’s config block before assuming test mode is active.
  • Some gateways do not support refunds or inquiry. Check src/Drivers/ before building an admin “refund” button. If unsupported, the driver may throw a BadMethodCallException or NotSupportedException.
  • SSL/CA issues are common in Docker images. If you see cURL error 60, install ca-certificates in the container; do not disable verification in production.
  • Config caching bites you. After changing credentials run php artisan config:clear. Stale config/gateway.php values are a frequent cause of “wrong merchant” errors.
  • Callback URL must be absolute and registered with the gateway. Many Iranian gateways require the callback URL to be whitelisted in their merchant panel; a mismatched GATEWAY_CALLBACK_URL will redirect to a blank gateway error page.
  • Timeouts. Iranian gateways can be slow; set a sensible timeout in your HTTP client config if the package exposes one. Don’t retry request() — you may create duplicate payment authorities. Retrying verify() is safe only if idempotent on your side.
  • Extension point: add a custom driver. If a gateway isn’t supported, subclass the package’s base driver (look for an abstract Driver or contract in src/) and register it under a custom key in config/gateway.php. This is usually cleaner than forking the package.
  • Version pinning. This package has a small maintainer footprint (24 stars). Pin to a known-good tag in composer.json and review diffs before upgrading — gateway APIs change without much notice, and breaking changes may not be well documented.
  • Reconciliation job. Schedule a daily job that scans pending transactions older than N minutes and calls the gateway’s inquiry (where supported) or manually flags them for review. Real customers lose connections; a payments table with only paid/unpaid will leak money.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package