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

Paypal Laravel Package

netshell/paypal

Laravel package for PayPal integration that provides a fluent wrapper around the PayPal API, with support for Express Checkout, recurring payments, transactions, and webhook/IPN handling to simplify common payment flows.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require netshell/paypal

This is a Laravel 5-era package, so on modern Laravel you will likely need to register the provider and facade manually in config/app.php (no auto-discovery). If it fails to install on your Laravel version, that is the first signal to stop and reconsider.

'providers' => [
    Netshell\Paypal\PaypalServiceProvider::class,
],

'aliases' => [
    'Paypal' => Netshell\Paypal\Facades\Paypal::class,
],

Publish and fill the config (credentials come from your PayPal account's API signature, under Profile → API Access):

php artisan vendor:publish --provider="Netshell\Paypal\PaypalServiceProvider"
// config/paypal.php
return [
    'username'  => env('PAYPAL_USERNAME'),
    'password'  => env('PAYPAL_PASSWORD'),
    'signature' => env('PAYPAL_SIGNATURE'),
    'sandbox'   => env('PAYPAL_SANDBOX', true),
];

Where to look first: vendor/netshell/paypal/src/. The package is a thin NVP wrapper — one class behind the facade. Read it end to end (it is short) to see exactly which Express Checkout calls are exposed and how the sandbox/live endpoint is chosen. Do not assume the README covers everything; the source is the contract.

First use case: a single-item checkout. Three routes and one controller:

  1. GET /checkout → build SetExpressCheckout params, call Paypal::setExpressCheckout($params), redirect the user to PayPal using the returned token/URL.
  2. GET /paypal/return (your RETURNURL) → read token and PayerID from the query string, call Paypal::getExpressCheckoutDetails($token) to confirm the payer.
  3. Same action → call Paypal::doExpressCheckoutPayment($token, $payerId, $params) and persist the result.

Start in sandbox, with a sandbox buyer account, before touching live credentials.

Implementation Patterns

Keep a single source of truth for the amount. The amount you send to SetExpressCheckout and the amount you send to DoExpressCheckoutPayment must match. Compute the cart total once, persist it against the token, and read it back on return. Drift here produces 10413 / 10414 errors that are annoying to diagnose.

Persist the token before redirecting. Create a payments row keyed on the PayPal token (unique index) with status, amount, currency, and your order reference. The return URL is a fresh request with no session continuity guarantee — everything you need must be in the DB or signed into the URL.

Wrap the facade in your own gateway. The facade is a singleton proxy; using it directly in controllers couples your app to a deprecated API. Define an interface (initiate, confirm, capture) and implement it with this package. When you migrate to PayPal Orders v2 you swap the implementation, not the call sites. This also gives you a seam to bind a fake in tests.

A typical controller action:

use Netshell\Paypal\Facades\Paypal;

public function start(Request $request)
{
    $order = Order::createFromCart(session('cart'));

    $params = [
        'RETURNURL' => route('paypal.return'),
        'CANCELURL' => route('paypal.cancel'),
        'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
        'PAYMENTREQUEST_0_AMT'          => number_format($order->total, 2, '.', ''),
        'PAYMENTREQUEST_0_CURRENCYCODE' => 'USD',
        'PAYMENTREQUEST_0_ITEMAMT'      => number_format($order->subtotal, 2, '.', ''),
        'L_PAYMENTREQUEST_0_NAME0'      => $order->description,
        'L_PAYMENTREQUEST_0_QTY0'       => 1,
        'L_PAYMENTREQUEST_0_AMT0'       => number_format($order->subtotal, 2, '.', ''),
    ];

    $response = Paypal::setExpressCheckout($params);
    abort_unless(($response['ACK'] ?? '') === 'Success', 502, 'PayPal init failed');

    $order->update(['paypal_token' => $response['TOKEN']]);

    return redirect(Paypal::getUrl($response['TOKEN'])); // or build the redirect from the token + sandbox flag
}

The exact redirect helper varies by version — confirm in the vendor source whether you get a full URL back or need to construct https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=... yourself.

Return handler: validate token matches your row, call getExpressCheckoutDetails for the payer name/email, then doExpressCheckoutPayment. Record PAYMENTINFO_0_TRANSACTIONID immediately — that is your receipt.

Guard against double-submission. Users refresh return URLs, browsers retry, and webhooks (none ship with this package) may or may not fire. Make the return handler idempotent: if the order is already paid, redirect to the success page without calling PayPal again.

Gotchas and Tips

  • NVP is a legacy API. Express Checkout over NVP is in long-term depreciation territory; PayPal steers new integrations to Orders v2 / Checkout. New sandbox accounts sometimes have trouble generating the classic API signature this package requires. If you cannot get credentials working, that is not a bug in your code — it is the package's ceiling. Treat this integration as a bridge, not a foundation.
  • Always check ACK. The wrapper returns the raw parsed NVP response array. Success and SuccessWithWarning are OK; anything else has L_ERRORCODE0 / L_LONGMESSAGE0. A surprising number of "PayPal is broken" reports are a 10417 (payer cannot pay) or 10413 (amount mismatch) being ignored.
  • Never send floats. Format with number_format($n, 2, '.', ''). Floating-point artifacts in AMT cause silent rejections. Line-item amounts plus ITEMAMT plus shipping/tax must reconcile to AMT or PayPal rejects the call wholesale.
  • Currency and country mismatches (e.g. a USD charge from a merchant account configured for another primary currency) surface as generic failures. Confirm the sandbox business account is set to the currency you send.
  • Config is cached at boot. Because credentials come from config, php artisan config:clear after any .env change. On a cached deployment, a stale signature produces opaque authentication failures.
  • Cancel is a valid outcome. CANCELURL fires with a token but no payer; mark the order abandoned, do not treat it as an error.
  • No webhook/IPN support. This package handles the synchronous redirect flow only. If you need asynchronous confirmation, refunds, or chargebacks, you are writing that yourself — and PayPal's IPN is itself deprecated, so plan for a REST webhook listener instead.
  • Testing: there are no fakes. Bind a stub behind your own gateway interface, and keep one sandbox end-to-end test to catch NVP payload regressions. Unit-test your amount/param builder separately, since that is where the real bugs live.
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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core