laravel-paypal

repository·main·Indexed 22 days ago

https://github.com/blendbyte/laravel-paypal

A PayPal REST API package for Laravel and standalone PHP projects. It provides high-level abstractions for PayPal's Orders v2, Subscriptions v2, and other APIs, including support for PayPal Fastlane, Pay Upon Invoice (DE/AT), and Payouts. The package includes a BillingPlanBuilder for complex recurring billing and supports custom PSR-18 HTTP clients.

Tokens
11.7K
Snippets
36
Records
39
Agent score
78%

What's inside laravel-paypal

  1. Use Laravel PayPal as a standalone PHP client

    main

    The package does not require Laravel. You can use it in any PHP project by instantiating the PayPal service and passing your credentials directly using setApiCredentials().

    use Srmklive\
    PayPal\Services\PayPal as PayPalClient;
    
    $provider = new PayPalClient;
    
    $provider->setApiCredentials([
        'mode' => 'sandbox', // or 'live'
        'sandbox' => [
            'client_id'     => 'YOUR_SANDBOX_CLIENT_ID',
            'client_secret' => 'YOUR_SANDBOX_CLIENT_SECRET',
            'app_id'        => 'APP-80W284485P519543T',
        ],
        'live' => [
            'client_id'     => 'YOUR_LIVE_CLIENT_ID',
            'client_secret' => 'YOUR_LIVE_CLIENT_SECRET',
            'app_id'        => 'YOUR_LIVE_APP_ID',
        ],
        'payment_action' => 'Sale',
        'currency'       => 'USD',
        'notify_url'     => '',
        'locale'         => 'en_US',
        'validate_ssl'   => true,
    ]);
    
    $provider->getAccessToken();
    
    // API methods are now available
    $order = $provider->createOrder([...]);
  2. Verify and Handle Webhooks

    main

    The library provides two ways to verify PayPal webhooks:

    1. API Roundtrip (verifyWebHook): Sends the payload and headers to PayPal to verify the signature. Best for maximum security.
    2. Local Verification (verifyWebHookLocally): Performs an offline RSA-SHA256 check using the signing certificate fetched from PayPal's PAYPAL-CERT-URL. This is faster and suitable for high-volume environments. Note that the request body must be the raw, unmodified bytes.

    Once verified, use the WebhookEvent class to parse the raw body and route events by type.

    // Verify locally (offline)
    $valid = $provider->verifyWebHookLocally(
        $request->headers->all(),
        'your-webhook-id',
        $request->getContent(), // must be the raw body bytes
    );
    
    // Handling events
    use Srmklive\PayPal\Events\WebhookEvent;
    
    $rawBody = $request->getContent();
    if ($provider->verifyWebHookLocally($request->headers->all(), 'your-webhook-id', $rawBody)) {
        $event = WebhookEvent::fromRawBody($rawBody);
    
        if ($event->is('PAYMENT.CAPTURE.COMPLETED')) {
            $this->handleCapture($event->resource);
        }
    }
  3. Implement the Subscriptions v2 recurring billing flow

    main

    To use the modern Subscriptions v2 API (replacing the sunsetting v1 Billing Agreements), use the helper methods to chain product and plan IDs before setting up the subscription.

    // New subscriptions flow
    $response = $provider->addProductById('PROD-XYAB12ABSB7868434')
        ->addBillingPlanById('P-5ML4271244454362WXNWU5NQ')
        ->setReturnAndCancelUrl('https://example.com/success', 'https://example.com/cancel')
        ->setupSubscription('John Doe', 'john@example.com');
    
    // Redirect the buyer to: $response['links'][href where rel === 'approve']
  4. Install Laravel PayPal

    main

    To use this package in a Laravel project, install it via Composer and then publish the configuration file to your application.

    composer require srmklive/paypal
    
    php artisan vendor:publish --provider "Srmklive\PayPal\Providers\PayPalServiceProvider"
  5. Handle PayPal API errors

    main

    By default, the provider returns errors as an array containing an error key.

    To use exceptions instead, call $provider->withExceptions(). This will cause API errors to throw a PayPalApiException. You can then catch the exception and use the following methods:

    • $e->getHttpStatus(): Returns the HTTP status code (e.g., 400, 404, 500).
    • $e->getMessage(): Returns the JSON-encoded error string.
    • $e->getPayPalError(): Returns the decoded error array or a plain string.

    Use $provider->withoutExceptions() to revert to the default silent mode.

    use Srmklive\PayPal\Exceptions\PayPalApiException;
    
    $provider->withExceptions();
    
    try {
        $order = $provider->showOrderDetails('bad-id');
    } catch (PayPalApiException $e) {
        $status = $e->getHttpStatus();
        $message = $e->getMessage();
        $paypalError = $e->getPayPalError();
    }
  6. Create subscriptions using Subscription Helpers

    main

    The provider offers a fluent API to create products and plans without manual payload construction. You can chain methods to define trial pricing, plan intervals (daily, weekly, monthly, annual, or custom), and setup fees.

    // Example: Monthly subscription with a 7-day trial
    $response = $provider->addProduct('Demo Product', 'Demo Product', 'SERVICE', 'SOFTWARE')
        ->addPlanTrialPricing('DAY', 7)
        ->addMonthlyPlan('Demo Plan', 'Demo Plan', 100)
        ->setReturnAndCancelUrl('https://example.com/paypal-success', 'https://example.com/paypal-cancel')
        ->setupSubscription('John Doe', 'john@example.com', '2025-01-01');
  7. Test PayPal Integrations with MockPayPalClient

    main

    Use MockPayPalClient to write unit tests without making real network calls to the PayPal sandbox.

    Basic Usage:

    1. Instantiate MockPayPalClient.
    2. Queue responses using addResponse($body, $status_code). If an operation returns an empty body (like a 204), pass false as the body.
    3. Use mockProvider() to get a pre-configured PayPal instance.

    Advanced Mocking:

    • Multiple Responses: Queue multiple responses; they are consumed sequentially per API call.
    • Inspecting Requests: Use lastRequest() to get the Psr\Http\Message\RequestInterface object to assert on headers, methods, or URIs. Use requests() to see all captured requests.
    • Injecting into existing providers: If you have an existing provider, use $provider->setClient($mock) to inject the mock client.
    use Srmklive//PayPal//Testing//MockPayPalClient;
    
    $mock = new MockPayPalClient();
    $mock->addResponse(['id' => '5O190127TN364715T', 'status' => 'CREATED']);
    
    // mockProvider() returns a ready PayPal instance
    $provider = $mock->mockProvider();
    $order = $provider->createOrder($data);
    
    expect($order['id'])->toBe('5O190127TN364715T');
    
    // Inspecting the request
    $request = $mock->lastRequest();
    $request->getMethod(); // 'POST'
  8. Use Pay Upon Invoice (Buy Now, Pay Later - DE/AT)

    main

    For merchants in Germany and Austria, you can enable 'Pay Upon Invoice'. This requires specific buyer details (name, email, birth date, phone, and billing address) to be set via setPaymentSourcePayUponInvoice() before calling createOrderWithPaymentSource().

    $provider->getAccessToken();
    
    $provider->setPaymentSourcePayUponInvoice([
        'name'       => ['given_name' => 'John', 'surname' => 'Doe'],
        'email'      => 'john.doe@example.com',
        'birth_date' => '1990-01-01',
        'phone'      => ['country_code' => '49', 'national_number' => '1234567890'],
        'billing_address' => [
            'address_line_1' => 'Hauptstraße 1',
            'admin_area_2'   => 'Berlin',
            'postal_code'    => '10115',
            'country_code'   => 'DE',
        ],
        'experience_context' => [
            'locale'     => 'de-DE',
            'return_url' => 'https://example.com/paypal-success',
            'cancel_url' => 'https://example.com/paypal-cancel',
        ],
    ]);
    
    $order = $provider->createOrderWithPaymentSource([
        'intent'         => 'CAPTURE',
        'purchase_units' => [
            ['amount' => ['currency_code' => 'EUR', 'value' => '99.00']],
        ],
    ]);
    
    $capture = $provider->capturePaymentOrder($order['id']);
  9. Implement PayPal Fastlane (Server-side)

    main

    PayPal Fastlane provides a one-click guest checkout. The server's role is to generate a client token and manage the Order v2 lifecycle.

    1. Generate Client Token: Call generateClientToken() and pass the resulting client_token to your frontend.
    2. Create Order: After the client-side Fastlane UI provides a single_use_token (from Fastlane.FastlaneCardComponent), call createOrder() with the payment_source containing that token.
    3. Capture Order: Use capturePaymentOrder($orderId) to finalize the transaction.
    // 1. Generate token
    $provider->getAccessToken();
    $result = $provider->generateClientToken();
    $clientToken = $result['client_token'];
    
    // 2. Create Order
    $order = $provider->createOrder([
        'intent' => 'CAPTURE',
        'purchase_units' => [
            ['amount' => ['currency_code' => 'USD', 'value' => '49.99']],
        ],
        'payment_source' => [
            'card' => [
                'single_use_token' => $singleUseToken, // From frontend
            ],
        ],
    ]);
    
    // 3. Capture
    $capture = $provider->capturePaymentOrder($order['id']);
    $captureId = $provider->getCaptureIdFromOrder($capture);
  10. Initialize the PayPal provider

    main

    You can initialize the PayPal client either by instantiating the PayPal service class directly or by using the PayPal facade in Laravel.

    use Srmklive//PayPal/Services/PayPal as PayPalClient;
    
    // Direct instantiation
    $provider = new PayPalClient;
    
    // Via Laravel Facade
    $provider = \PayPal::setProvider();
    use Srmklive\PayPal\Services\PayPal as PayPalClient;
    
    $provider = new PayPalClient;
    
    // Or via facade
    $provider = \PayPal::setProvider();
  11. Implement the Orders v2 redirect-based payment flow

    main

    To use the modern Orders v2 API (replacing the sunsetting v1 Payments API), follow a two-step process: 1. Create the order and redirect the buyer to the PayPal approval URL. 2. Capture the payment after the buyer approves.

    // 1. Create the order and redirect the buyer
    $order = $provider->createOrder([
        'intent' => 'CAPTURE',
        'purchase_units' => [
            ['amount' => ['currency_code' => 'USD', 'value' => '49.99']],
        ],
        'payment_source' => [
            'paypal' => [
                'experience_context' => [
                    'return_url' => 'https://example.com/paypal/return',
                    'cancel_url' => 'https://example.com/paypal/cancel',
                ],
            ],
        ],
    ]);
    
    // Redirect the buyer to: $order['links'][href where rel === 'payer-action']
    
    // 2. After the buyer approves, capture the payment
    $capture = $provider->capturePaymentOrder($order['id']);
    $captureId = $provider->getCaptureIdFromOrder($capture); // store this
  12. Configure Laravel PayPal via environment variables

    main

    In a Laravel application, you can configure the PayPal provider using the following .env keys. These map to the config/paypal.php file.

    PAYPAL_MODE=sandbox
    PAYPAL_SANDBOX_CLIENT_ID=
    PAYPAL_SANDBOX_CLIENT_SECRET=
    PAYPAL_LIVE_CLIENT_ID=
    PAYPAL_LIVE_CLIENT_SECRET=
    PAYPAL_LIVE_APP_ID=
    
    # Optional
    PAYPAL_TIMEOUT=30
    PAYPAL_CONNECT_TIMEOUT=10
    PAYPAL_MAX_RETRIES=2