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

Pay Laravel Package

ibrand/pay

Laravel payment package offering a unified, configurable API for integrating common payment gateways into PHP applications. It helps manage payment requests, gateway settings, and callback handling, reducing boilerplate for adding checkout and payment flows to Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Laravel wrapper around a WeChat/Alipay SDK (most likely yansongda/pay), so the fastest path is to understand what it wraps before writing any business code.

  1. Install and inspect the dependency tree

    composer require ibrand/pay
    composer show -t ibrand/pay   # see whether yansongda/pay, overtrue/wechat, etc. are underneath
    

    The dependency tree tells you which SDK API surface is actually available — the wrapper usually forwards calls to it.

  2. Publish and read the config. With little documentation, read the service provider to find the publish tag:

    php artisan vendor:publish --provider="Ibrand\Pay\PayServiceProvider"
    

    Open config/pay.php and map every key to an env var: app_id, mch_id, key, notify_url, return_url, and absolute cert paths for WeChat refunds/transfers.

  3. First use case: a 0.01 scan/web payment. Register credentials in .env, then hit the facade from a tinker session or a throwaway route:

    use Ibrand\Pay\Facades\Pay; // verify exact namespace in vendor/ibrand/pay/src
    
    $order = [
        'out_trade_no' => 'TEST' . time(),
        'total_amount' => '0.01',   // Alipay: yuan string
        'subject'      => 'Test order',
    ];
    
    return Pay::alipay()->web($order); // returns a redirect/HTML response
    

    The response is a Laravel Response — return it directly from the controller. If you get a signature error, you are almost certainly on a key/cert config problem, not a code problem.

Where to look first, in order: config/pay.php → the service provider → the facade class → the underlying SDK's own docs (which are far better than this package's).

Implementation Patterns

Single payment service, not facades in controllers. Wrap the facade in one PaymentService so channel selection, order creation, and response formatting live in one place. Controllers should call $payments->create($order, 'alipay') and nothing else. This makes it trivial to swap or fork the package later.

Split create / notify / return into three routes.

  • POST /pay/{channel} — creates the order, redirects to the gateway.
  • POST /pay/notify/{channel} — server-to-server callback. This is the only place you mark an order paid.
  • GET /pay/return/{channel} — user-facing redirect. Use it for UX only; never trust it to flip order state.

Callback handler skeleton (the part that matters):

public function notify(Request $request, string $channel)
{
    $data = $request->all();

    // 1. Verify signature via the SDK — do not hand-roll this.
    // 2. Look up order by out_trade_no; 404 if missing.
    // 3. Verify amount matches your stored amount (yuan vs fen — see gotchas).
    // 4. Idempotency: if already paid, return success immediately.
    // 5. Mark paid inside a DB transaction, then dispatch your fulfilment job.

    return response('success'); // WeChat & Alipay both expect literal 'success'
}

Use a state machine on the order. pending → paid → refunding → refunded → closed. Callbacks are retried by both gateways for hours/days, so the idempotency guard on paid is not optional.

Refunds are a separate, manual flow. WeChat refunds require the cert files configured as absolute paths; Alipay refunds need out_request_no for partial/duplicate refund safety. Test refunds against the sandbox before shipping — they are the most common production incident with these gateways.

Per-tenant credentials. If you have multiple merchants, avoid mutating global config at runtime. Most wrappers accept a config array per call (Pay::alipay($config) or Pay::config($config)), which is safer than config()->set() mid-request.

Gotchas and Tips

  • CSRF will silently kill every callback. Add pay/notify/* to VerifyCsrfToken::$except (or the middleware group equivalent). This is the #1 reason "the payment succeeded but the order stayed pending."
  • Return success as plain text, not JSON. Both WeChat and Alipay retry until they see the literal string. Returning {"code":0} guarantees endless retries.
  • Amount units differ. Alipay uses a yuan string ("0.01"), WeChat uses integer fen (1). Verify the amount in the callback against what you stored — never trust the gateway's amount alone.
  • Cert and key paths must be absolute. WeChat especially resolves relative paths from unpredictable working directories. Use storage_path('certs/...') and store certs outside the repo.
  • Signature failures are config failures. app_id mismatch, invalid sign, invalid total_fee all trace back to wrong key/cert/app_id pairs, or to a mismatch between the key type (RSA vs RSA2) and what's configured.
  • The package is low-traffic (13 stars, 0 dependents). Pin an exact version in composer.json, and read vendor/ibrand/pay/src before relying on anything undocumented. Budget for a fork: the wrapper is usually a few hundred lines, so forking to fix a namespace or add a channel is realistic.
  • Log raw request/response pairs. Before going live, dump $request->all() on notify and the full gateway response on create/refund into a dedicated payments log channel. Signing bugs and amount mismatches are unreproducible without payloads.
  • Use the sandboxes. Alipay's sandbox gateway and WeChat's sandbox (sandboxnew) are separate base URLs — confirm whether the package exposes a sandbox switch in config; if not, override the gateway URL at the SDK level.
  • Extension points are limited. If the facade doesn't expose a call you need (e.g. WeChat v3 JSAPI, profit-sharing), drop down to the underlying SDK class via the container rather than patching the package — your PaymentService is the seam that keeps that contained.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons