futureecom / omnipay-paypal
PayPal driver for the Omnipay payment processing library.
Requires
- php: ^8.3
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^7.0
- omnipay/common: ^3.0
- php-http/guzzle7-adapter: ^1.0
- psr/simple-cache: ^3.0
- symfony/http-client: ^7.1
Requires (Dev)
- http-interop/http-factory-guzzle: ^1.2
- laravel/pint: ^1.2
- omnipay/tests: ^4.1
- php-http/mock-client: ^1.6
- phpunit/phpunit: ^9.6
- rector/rector: ^0.14.6
- symfony/var-dumper: ^5.0
This package is auto-updated.
Last update: 2026-07-28 15:01:13 UTC
README
PayPal gateway for the Omnipay payment processing library
This standalone package implements PayPal Orders v2 and Vault/Payment Method Tokens v3 for Omnipay using PSR-compatible HTTP and cache interfaces.
Installation
Install the gateway using Composer:
composer require futureecom/omnipay-paypal
Configuration
First, you need to obtain your PayPal API credentials:
- Go to PayPal Developer Dashboard
- Create or select your app
- Copy your Client ID and Client Secret
Usage
Initialize the Gateway
use Omnipay\Omnipay; $gateway = Omnipay::create('PayPal'); $gateway->setClientId('your-client-id'); $gateway->setClientSecret('your-client-secret'); $gateway->setTestMode(true); // Set to false for live transactions
OAuth tokens are cached in memory by default and are isolated by environment and client ID. Long-running or multi-process applications can provide any PSR-16 cache:
/** @var \Psr\SimpleCache\CacheInterface $cache */ $gateway->setCache($cache);
The gateway retries a PayPal operation once when PayPal rejects an expired access token with HTTP 401.
Create an Authorization (Auth Only)
Creates a PayPal order with intent to authorize. The customer must approve the order before you can authorize.
$response = $gateway->authorize([ 'amount' => '100.00', 'currency' => 'USD', 'returnUrl' => 'https://example.com/payment/return', 'cancelUrl' => 'https://example.com/payment/cancel', 'transactionId' => 'ORDER-123', 'description' => 'Order #123', 'brandName' => 'My Store', ])->send(); if ($response->isRedirect()) { // Redirect the customer to PayPal for approval $redirectUrl = $response->getRedirectUrl(); $orderId = $response->getTransactionReference(); // Save this! // Redirect to $redirectUrl } else { echo "Error: " . $response->getMessage(); }
Create a Purchase (Auth + Capture)
Creates a PayPal order with intent to capture. Similar flow as authorize.
$response = $gateway->purchase([ 'amount' => '50.00', 'currency' => 'USD', 'returnUrl' => 'https://example.com/payment/return', 'cancelUrl' => 'https://example.com/payment/cancel', ])->send(); if ($response->isRedirect()) { $redirectUrl = $response->getRedirectUrl(); $orderId = $response->getTransactionReference(); // Redirect to $redirectUrl }
For non-vaulted Standard Checkout, provide explicit identifiers and PayPal experience context. No Vault v3 operation or vault attribute is used:
$response = $gateway->purchase([ 'amount' => '100.00', 'currency' => 'ILS', 'referenceId' => 'internal-attempt-id', 'invoiceId' => 'unique-invoice-id', 'customId' => 'cart:cart-id', 'payPalRequestId' => 'stable-create-idempotency-key', 'returnUrl' => 'https://example.com/paypal/return', 'cancelUrl' => 'https://example.com/paypal/cancel', 'userAction' => 'PAY_NOW', 'shippingPreference' => 'NO_SHIPPING', ])->send();
Retrieve an Order
Retrieve an order after an interrupted or ambiguous capture:
$response = $gateway->fetchOrder([ 'transactionReference' => $orderId, ])->send();
Advanced Checkout and Vault v3
Advanced integrations can create and confirm Vault v3 setup tokens, retrieve or
delete payment tokens, and then pass a token as cardReference to an Orders v2
request. These operations are opt-in; Orders v2 requests do not invoke Vault v3
automatically.
$setup = $gateway->createSetupToken([ 'paymentSource' => ['card' => []], 'payPalRequestId' => 'stable-setup-token-key', ])->send(); $token = $gateway->confirmSetupToken([ 'transactionReference' => $setup->getSetupTokenId(), 'payPalRequestId' => 'stable-confirm-key', ])->send();
Verify a REST Webhook
The library verifies signatures through PayPal. Persisting and reconciling events remains the consuming application's responsibility.
$response = $gateway->verifyWebhookSignature([ 'webhookId' => $webhookId, 'transmissionId' => $headers['PAYPAL-TRANSMISSION-ID'], 'transmissionTime' => $headers['PAYPAL-TRANSMISSION-TIME'], 'certUrl' => $headers['PAYPAL-CERT-URL'], 'authAlgo' => $headers['PAYPAL-AUTH-ALGO'], 'transmissionSignature' => $headers['PAYPAL-TRANSMISSION-SIG'], 'webhookEvent' => $decodedEvent, ])->send(); if (!$response->isWebhookSignatureValid()) { // Reject the event. }
Capture an Order
After the customer approves the order, capture the payment:
$response = $gateway->capture([ 'transactionReference' => $orderId, // Order ID from authorize/purchase ])->send(); if ($response->isSuccessful()) { echo "Payment captured successfully!"; $captureId = $response->getCaptureId(); // Save for refunds } else { echo "Capture failed: " . $response->getMessage(); }
Partial Capture
$response = $gateway->capture([ 'transactionReference' => $orderId, 'amount' => '50.00', 'currency' => 'USD', ])->send();
Refund a Payment
Refund a captured payment using the capture ID:
// Full refund $response = $gateway->refund([ 'transactionReference' => $captureId, // Capture ID from capture response ])->send(); // Partial refund $response = $gateway->refund([ 'transactionReference' => $captureId, 'amount' => '25.00', 'currency' => 'USD', ])->send(); if ($response->isSuccessful()) { echo "Refund successful!"; echo "Refund ID: " . $response->getTransactionReference(); } else { echo "Refund failed: " . $response->getMessage(); }
Void an Authorization
Void an authorized payment (must be done before capture):
$response = $gateway->void([ 'transactionReference' => $authorizationId, // Authorization ID ])->send(); if ($response->isSuccessful()) { echo "Authorization voided successfully!"; }
Complete Payment Flow Example
use Omnipay\Omnipay; // 1. Initialize gateway $gateway = Omnipay::create('PayPal'); $gateway->setClientId('your-client-id'); $gateway->setClientSecret('your-client-secret'); $gateway->setTestMode(true); // 2. Create order $response = $gateway->purchase([ 'amount' => '99.99', 'currency' => 'USD', 'returnUrl' => 'https://example.com/payment/complete', 'cancelUrl' => 'https://example.com/payment/cancel', 'transactionId' => 'ORDER-456', 'description' => 'Premium subscription', 'brandName' => 'My Store', 'locale' => 'en-US', 'landingPage' => 'LOGIN', 'userAction' => 'PAY_NOW', ])->send(); // 3. Redirect customer to PayPal if ($response->isRedirect()) { // Store order ID in session $_SESSION['paypal_order_id'] = $response->getTransactionReference(); header('Location: ' . $response->getRedirectUrl()); exit; } // 4. On return URL, capture the payment $orderId = $_SESSION['paypal_order_id']; $captureResponse = $gateway->capture([ 'transactionReference' => $orderId, ])->send(); if ($captureResponse->isSuccessful()) { // Payment successful! $captureId = $captureResponse->getCaptureId(); // Store capture ID for potential refunds // Update order status // Send confirmation email } else { // Handle error $error = $captureResponse->getMessage(); }
Response Methods
All responses support these methods:
| Method | Description |
|---|---|
isSuccessful() |
Whether the request was successful |
isRedirect() |
Whether a redirect is required |
getRedirectUrl() |
The PayPal approval URL |
getTransactionReference() |
Order ID or Refund ID |
getAuthorizationId() |
Authorization ID (after authorize) |
getCaptureId() |
Capture ID (after capture) |
getMessage() |
Error message if failed |
getCode() |
Status code (CREATED, COMPLETED, etc.) |
getHttpStatus() |
PayPal HTTP response status |
getDebugId() |
PayPal diagnostic debug ID |
getDetails() |
Structured PayPal error details |
getIssues() |
PayPal issue codes |
isWebhookSignatureValid() |
Whether PayPal verified a REST webhook |
getData() |
Full response data array |
Sandbox Testing
- Create sandbox accounts at PayPal Developer Dashboard
- Use sandbox credentials with
setTestMode(true) - Test with sandbox buyer accounts
Support
If you have any questions or issues, please open an issue on GitHub.
License
This package is open-sourced software licensed under the MIT license.