omnipay/paypal
PayPal gateway driver for the Omnipay PHP payments library. Supports Express Checkout (including In-Context), Website Payments Pro, and PayPal REST API. Install via Composer and use with Omnipay for framework-agnostic payment processing.
composer require omnipay/paypal
config/services.php:
'paypal' => [
'client_id' => env('PAYPAL_CLIENT_ID'),
'secret' => env('PAYPAL_SECRET'),
'sandbox' => env('PAYPAL_SANDBOX', false),
'rest_endpoint' => env('PAYPAL_REST_ENDPOINT', 'https://api.paypal.com'),
],
app/Services/PayPalService.php):
use Omnipay\Omnipay;
class PayPalService {
public function __construct() {
$this->gateway = Omnipay::create('PayPal_Rest');
$this->gateway->setClientId(config('services.paypal.client_id'));
$this->gateway->setSecret(config('services.paypal.secret'));
$this->gateway->setTestMode(config('services.paypal.sandbox'));
}
public function getGateway() {
return $this->gateway;
}
}
AppServiceProvider:
public function register() {
$this->app->singleton(PayPalService::class);
}
$gateway = app(PayPalService::class)->getGateway();
$request = $gateway->purchase([
'amount' => '10.00',
'currency' => 'USD',
'returnUrl' => route('paypal.return'),
'cancelUrl' => route('paypal.cancel'),
]);
$response = $request->send();
$response->redirect(); // Redirects to PayPal
PaypalReturnController):
$gateway = app(PayPalService::class)->getGateway();
$request = $gateway->completePurchase([
'token' => $request->query->get('token'),
]);
$response = $request->send();
if ($response->isSuccessful()) {
// Save transaction to DB
$transaction = $response->getTransactionReference();
// Mark order as paid
}
purchase().completePurchase().transactionReference (from completePurchase() response) to DB.orders table with paypal_transaction_id field.// app/Http/Controllers/PaypalController.php
public function return(Request $request) {
$gateway = app(PayPalService::class)->getGateway();
$response = $gateway->completePurchase([
'token' => $request->token,
])->send();
if ($response->isSuccessful()) {
Order::find($request->order_id)->update([
'paid_at' => now(),
'transaction_id' => $response->getTransactionReference(),
]);
}
return redirect()->route('order.success');
}
PayPal_Rest gateway.$gateway = app(PayPalService::class)->getGateway();
$request = $gateway->refund([
'amount' => '5.00',
'transactionReference' => 'PAY-123ABC', // From DB
]);
$response = $request->send();
if ($response->isSuccessful()) {
// Update order status
}
// app/Http/Middleware/VerifyPayPalWebhook.php
public function handle($request, Closure $next) {
$gateway = app(PayPalService::class)->getGateway();
$request->validate([
'auth_algo' => 'required',
'cert_url' => 'required',
'txn_id' => 'required',
'txn_type' => 'required',
'notify_version' => 'required',
'verify_sign' => 'required',
]);
$verified = $gateway->validateWebhook($request->all());
if (!$verified) {
abort(403, 'Invalid PayPal webhook signature');
}
return $next($request);
}
app/Http/Kernel.php:
protected $routeMiddleware = [
'verify.paypal' => \App\Http\Middleware\VerifyPayPalWebhook::class,
];
// app/Services/PayPalService.php
public function handlePayment($requestData) {
try {
$response = $this->gateway->purchase($requestData)->send();
return $response;
} catch (\Omnipay\Common\Exception\InvalidResponseException $e) {
\Log::error('PayPal error: ' . $e->getMessage());
throw new \App\Exceptions\PaymentFailedException('Payment processing failed');
}
}
PayPal_Rest for New Projects:
PayPal_Express/PayPal_Pro (deprecated APIs). Focus on REST for future compatibility.// Dispatch a job after refund
RefundProcessed::dispatch($order, $response);
Schema::create('paypal_transactions', function (Blueprint $table) {
$table->id();
$table->string('transaction_reference');
$table->string('status');
$table->json('metadata');
$table->timestamps();
});
'sandbox' => env('PAYPAL_SANDBOX', true),
Transaction Reference Mismatch:
purchase() returns a token, but completePurchase() returns the actual transactionReference. Always use the latter for refunds.transactionReference from completePurchase() in your DB.
$transactionRef = $response->getTransactionReference(); // Correct for refunds
Deprecated Classic APIs:
PayPal_Express and PayPal_Pro use PayPal Classic APIs, which are end-of-life. PayPal may block requests.PayPal_Rest exclusively. If you must use Classic APIs, monitor PayPal’s deprecation timeline and migrate.Webhook Validation:
openssl_verify for IPN or PayPal’s REST API signature headers).DateTime Formatting:
EXPDATE and STARTDATE as 6-digit strings (e.g., 1225 for December 2025).$expiryDate = '1225'; // YYMM format
Recurring Payments Not Supported:
TLS 1.2 Requirement:
// In bootstrap/app.php
How can I help you explore Laravel packages today?