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

Cashier Laravel Package

vandarpay/cashier

A Laravel integration for the Vandar payment gateway, providing a fluent, Cashier-style API to manage payments, verify transactions, and handle common gateway workflows in PHP applications. It helps developers add Vandar payment support to Laravel projects with minimal setup and clean, readable code...

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require vandarpay/cashier
php artisan vendor:publish --provider="Vandar\Cashier\CashierServiceProvider"
php artisan migrate

The package ships a config file (typically config/vandar.php or config/cashier.php) and a transactions/direct-debit migration. Publish both before running anything — the migration is what backs callback reconciliation.

Environment

VANDAR_API_KEY=your-api-key
VANDAR_BUSINESS=your-business-slug
VANDAR_CALLBACK_URL=https://app.test/payment/callback

The API key and business identifier are issued in the Vandar panel. Callback URLs are often allowlisted on the gateway side, so register the exact production and staging URLs there before testing — this is the single most common reason a "correct" integration silently fails to return.

Where to look first

  1. config/vandar.php — endpoint base URLs, sandbox flag, amount unit, callback path.
  2. The published migration — know the exact table/column names before you write reconciliation logic.
  3. src/ — this is an 11-star package; the source is short and is the real documentation. Read the payment/token classes end to end before trusting any blog post.
  4. README.md examples — treat the method names there as authoritative, not the ones you remember from Cashier/Stripe.

First use case: one-off IPG payment

The typical day-one flow: user clicks "pay", you create a payment record, redirect to the gateway, and the gateway POSTs back to your callback. In rough outline:

// Initiate
$payment = $user->checkout($amountInRial)   // confirm exact signature in src/
    ->withCallback(route('payment.callback'))
    ->create();

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

// Callback
$result = Cashier::verify($request->token);
if ($result->isSuccessful()) {
    // mark order paid, idempotently
}

Get the redirect-and-verify round trip working with a 1,000 Rial payment in sandbox before touching direct debit. Verification is the part that actually matters — a redirect alone proves nothing.

Implementation Patterns

Payment initiation behind an action/service class. Keep gateway calls out of controllers. A CreatePaymentAction that persists a transaction row before calling the gateway gives you a local record keyed by your own reference even if the HTTP call times out — which is exactly the case you'll need to debug later.

Callback controller, outside auth. Put the callback route in routes/web.php with CSRF excluded for that URI, and do not assume a session. Vandar's callback arrives as a browser POST from the gateway; the user's session cookie usually survives, but you should not depend on it. Resolve the order from the token/transaction row you stored, not from auth()->user().

Idempotent fulfilment. The callback can be replayed (user refreshes, gateway retries). Wrap fulfilment in a DB transaction with a status guard:

DB::transaction(function () use ($token) {
    $tx = Transaction::where('token', $token)->lockForUpdate()->firstOrFail();
    if ($tx->status === 'paid') return;      // already handled
    Cashier::verify($token);                  // server-to-server check
    $tx->markPaid();
    Order::find($tx->order_id)->fulfil();
});

Never trust the callback query string. The POST parameters tell you which payment to look up; the verify call tells you whether it succeeded. Always verify server-side before shipping goods.

Direct debit as a separate workflow. IPG is a one-shot redirect/verify. Direct debit is a token-based mandate: you request a token/mandate for the customer, they approve it once, and then you can initiate withdrawals against that token without them being present. Model it as its own table/entity (direct_debit_tokens or whatever the migration names it) linked to your User. There is no scheduler in the package — you own the cron/queue job that decides when to withdraw. Treat it as "stored payment authority", not "subscription billing".

Wrap it, don't scatter it. Because the package is small and likely to change shape, put it behind your own interface (PaymentGateway) with request(), verify(), withdraw(). Swapping gateways later then means one adapter, and you can fake the gateway in tests without mocking the package's internals.

Testing. Point the config's base URL at a local stub (or use Http::fake()), and assert on your own transaction rows rather than the package's return objects. Snapshot one real sandbox response body into a fixture — the field names (status, transId, amount) are what your assertions should key off.

Gotchas and Tips

Amount units are the classic bug. Vandar's IPG API works in Rial; Iranian businesses and users think in Toman. A 100,000 Toman cart is 1,000,000 Rial. Decide on one unit at your domain boundary (recommend: store Rial, format Toman in views) and convert in exactly one place. Mismatched units produce payments that succeed with the wrong amount — worse than failures, because they're hard to notice.

Config caching. After changing VANDAR_* values, run php artisan config:clear. In production with config:cache, env edits do nothing until you re-cache. This alone accounts for a large share of "it works locally" tickets.

Verify responses are not exceptions. Gateway verify calls usually return a payload with a status flag rather than throwing on a declined payment. Check the flag; don't rely on try/catch to detect failure. Conversely, do wrap the call in try/catch for transport errors and log the raw response body — you will need it.

Log the full exchange. Request payload (minus the API key), HTTP status, and response body, tagged with your transaction reference. When a customer says "it took my money", this log is the only thing that resolves it quickly.

Callback URL must be absolute and public. route() inside a queued job can produce the wrong host. Hardcode the callback URL in config or set APP_URL correctly everywhere. Also confirm the URL is registered in the Vandar panel, including the port if you're tunnelling for local dev.

Sandbox flag. If the config has a sandbox/development switch, verify it's off in production and that the base URL actually changed — a sandbox flag that only alters redirect URLs but not API hosts gives you fake successes.

Don't build recurring billing on top of IPG. Users must be redirected each time. If you need unattended charging, that's the direct-debit path, and it requires the customer's explicit mandate — plan the UX for mandate capture and for mandate revocation.

Small-package risk. With 11 stars, expect thin docs, few maintainers, and possible breaking changes in patch releases. Pin an exact version ("vandarpay/cashier": "1.x.y"), commit the lockfile, and be prepared to fork. The upside: the surface area is small enough to read in an afternoon, so you're never truly blocked.

Extension points. If the package exposes events (payment initiated/verified), listen rather than editing the package — it makes upgrades survivable. If it doesn't, your own action classes are the extension point.

Currency formatting. Store and compute in integers; format with Latin digits for API payloads and Persian digits only in the UI. Mixing Persian numerals into an amount field sent to the gateway will fail or, worse, be parsed as a different number.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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